-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #75 from newrelic/filter-bar
feat: filter bar component
- Loading branch information
Showing
21 changed files
with
1,000 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# FilterBar | ||
|
||
The FilterBar component allows a user to filter from a list of options. Based on the choices of the user, the component returns a NRQL query `WHERE` clause. | ||
|
||
## Usage | ||
|
||
To use the FilterBar component in your project, follow these steps: | ||
|
||
Import the component: | ||
|
||
```jsx | ||
import { FilterBar } from '@newrelic/nr-labs-components'; | ||
``` | ||
|
||
Use the component in your code: | ||
|
||
```jsx | ||
<FilterBar | ||
options={optionsArray} | ||
onChange={(whereClause) => fnToHandleChange()} | ||
getValues={fnToGetAdditionalValues} | ||
/> | ||
``` | ||
|
||
### Props | ||
|
||
The FilterBar component accepts the following props: | ||
|
||
- `options` (array) - an array of option objects. See below for object properties. | ||
- `onChange` (function) - a callback function that receives a string formatted as WHERE clauses for a NRQL query. | ||
- `getValues` (function) - an aysnc function that fetches values that match user input. | ||
|
||
#### `options` object | ||
|
||
- `option` (string) - the title for the option | ||
- `type` (string) - option type - either `string` or `numeric` | ||
- `values` (array) - array of values for the option | ||
|
||
#### `getValues` | ||
|
||
The async function passed to `getvalues` is called in two scenarios. | ||
|
||
- When an empty array is passed for `values` for an option, and the user clicks on the option to expand the list of values for that option. The function is called with just one attribute - `option` and expects an array of values to be returned. | ||
- When the user types out a value in the search field for the option. The attributes passed are the `option` and a `searchString` formatted as a NRQL WHERE clause. | ||
|
||
## Example | ||
|
||
Here's an example of how to use the FilterBar component: | ||
|
||
```jsx | ||
import React from 'react'; | ||
import { FilterBar } from '@newrelic/nr-labs-components'; | ||
|
||
const options = [ | ||
{ | ||
option: 'responseCode', | ||
type: 'numeric', | ||
values: ['200', '404'], | ||
}, | ||
{ | ||
option: 'scheme', | ||
type: 'string', | ||
values: ['https', 'http'], | ||
}, | ||
// ... more filter options | ||
]; | ||
|
||
const getValues = async (option, searchString) => { | ||
console.log(`getValues was called for option ${option}`); | ||
if (searchString) console.log(`SELECT attribute FROM event ${searchString}`); | ||
// query for values and return values as an array | ||
return []; | ||
}; | ||
|
||
function App() { | ||
const changeHandler = (whereClause) => { | ||
console.log(`SELECT * FROM Transaction WHERE ${whereClause}`); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<h1>FilterBar</h1> | ||
<FilterBar | ||
options={options} | ||
onChange={changeHandler} | ||
getValues={getValues} | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
export default App; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import React, { useEffect, useRef, useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import styles from './styles.scss'; | ||
|
||
const Conjunction = ({ operator, isHint, onChange }) => { | ||
const [showPicker, setShowPicker] = useState(false); | ||
const thisComponent = useRef(); | ||
|
||
useEffect(() => { | ||
function handleClicksOutsideComponent(evt) { | ||
if ( | ||
showPicker && | ||
thisComponent && | ||
!thisComponent.current.contains(evt.target) | ||
) | ||
setShowPicker(false); | ||
} | ||
document.addEventListener('mousedown', handleClicksOutsideComponent); | ||
|
||
return function cleanup() { | ||
document.removeEventListener('mousedown', handleClicksOutsideComponent); | ||
}; | ||
}); | ||
|
||
const clickHandler = (evt) => { | ||
evt.preventDefault(); | ||
evt.stopPropagation(); | ||
setShowPicker(!showPicker); | ||
}; | ||
|
||
const changeHandler = (selection, evt) => { | ||
evt.preventDefault(); | ||
evt.stopPropagation(); | ||
if (onChange && selection !== operator) onChange(selection); | ||
}; | ||
|
||
const options = ['AND', 'OR']; | ||
|
||
return ( | ||
<span | ||
className={`${styles.conjunction} ${isHint ? styles.hint : ''}`} | ||
onClick={clickHandler} | ||
ref={thisComponent} | ||
> | ||
{operator} | ||
{showPicker && ( | ||
<span className={styles['conjunction-picker']}> | ||
{options.map((opt, i) => ( | ||
<span | ||
key={i} | ||
className={opt === operator ? styles.selected : ''} | ||
onClick={(evt) => changeHandler(opt, evt)} | ||
> | ||
{opt} | ||
</span> | ||
))} | ||
</span> | ||
)} | ||
</span> | ||
); | ||
}; | ||
|
||
Conjunction.propTypes = { | ||
operator: PropTypes.string, | ||
isHint: PropTypes.bool, | ||
onChange: PropTypes.func, | ||
}; | ||
|
||
export default Conjunction; |
37 changes: 37 additions & 0 deletions
37
src/components/filter-bar/components/conjunction/styles.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
.conjunction { | ||
padding: 1px 4px; | ||
background: rgb(231, 233, 233); | ||
border-radius: 3px; | ||
margin-right: 8px; | ||
color: rgb(83, 94, 101); | ||
cursor: pointer; | ||
position: relative; | ||
display: inline-block; | ||
|
||
&.hint { | ||
background: rgba(231, 233, 233, 0.5); | ||
color: rgba(83, 94, 101, 0.5); | ||
} | ||
|
||
.conjunction-picker { | ||
position: absolute; | ||
background: #ffffff; | ||
box-shadow: 0px 4px 4px 4px rgba(0, 0, 0, 0.02), | ||
0px 8px 16px 8px rgba(2, 3, 3, 0.05); | ||
border-radius: 4px; | ||
display: flex; | ||
gap: 3px; | ||
padding: 5px; | ||
z-index: 11; | ||
|
||
span { | ||
padding: 1px 4px; | ||
background: #ffffff; | ||
border-radius: 3px; | ||
|
||
&.selected { | ||
background: #e8e8e8; | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import Conjunction from './conjunction'; | ||
import Label from './label'; | ||
import Value from './value'; | ||
|
||
export { Conjunction, Label, Value }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import { RemoveIcon } from '../../icons'; | ||
|
||
import styles from './styles.scss'; | ||
|
||
const Label = ({ value, onRemove }) => { | ||
const removeClickHandler = (evt) => { | ||
evt.stopPropagation(); | ||
if (onRemove) onRemove(evt); | ||
}; | ||
|
||
return ( | ||
<span className={styles.label}> | ||
<span className={styles['label-text']}>{value}</span> | ||
<span className={styles['label-remove']} onClick={removeClickHandler}> | ||
<img src={RemoveIcon} alt="remove" /> | ||
</span> | ||
</span> | ||
); | ||
}; | ||
|
||
Label.propTypes = { | ||
value: PropTypes.string, | ||
onRemove: PropTypes.func, | ||
}; | ||
|
||
export default Label; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
.label { | ||
display: inline-flex; | ||
flex-direction: row; | ||
justify-content: center; | ||
align-items: center; | ||
padding: 1px 4px; | ||
background-color: #e1edff; | ||
border-radius: 3px; | ||
color: #0b6acb; | ||
margin-right: 8px; | ||
margin-bottom: 6px; | ||
|
||
.label-text { | ||
flex: none; | ||
order: 0; | ||
flex-grow: 0; | ||
} | ||
|
||
.label-remove { | ||
margin-left: 4px; | ||
cursor: pointer; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
import styles from './styles.scss'; | ||
|
||
const Value = ({ value, width, optionIndex, valueIndex, onChange }) => { | ||
const changeHandler = () => | ||
onChange ? onChange(optionIndex, valueIndex) : null; | ||
|
||
return ( | ||
<div className={styles['option-value']} style={{ width }}> | ||
<input | ||
type="checkbox" | ||
id={`nrlabs-filter-bar-checkbox-${value.id}-${valueIndex}`} | ||
checked={value.isSelected} | ||
onChange={changeHandler} | ||
/> | ||
<label htmlFor={`nrlabs-filter-bar-checkbox-${value.id}-${valueIndex}`}> | ||
{value.display} | ||
</label> | ||
</div> | ||
); | ||
}; | ||
|
||
Value.propTypes = { | ||
value: PropTypes.object, | ||
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | ||
optionIndex: PropTypes.number, | ||
valueIndex: PropTypes.number, | ||
onChange: PropTypes.func, | ||
}; | ||
|
||
export default Value; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
.option-value { | ||
align-items: center; | ||
display: inline-flex; | ||
position: relative; | ||
|
||
label { | ||
padding-left: 8px; | ||
overflow: hidden; | ||
white-space: nowrap; | ||
text-overflow: ellipsis; | ||
} | ||
|
||
.option-picker { | ||
position: absolute; | ||
background: #ffffff; | ||
box-shadow: 0px 4px 4px 4px rgba(0, 0, 0, 0.02), | ||
0px 8px 16px 8px rgba(2, 3, 3, 0.05); | ||
border-radius: 4px; | ||
display: flex; | ||
gap: 3px; | ||
padding: 5px; | ||
top: 100%; | ||
z-index: 1; | ||
|
||
span { | ||
padding: 1px 4px; | ||
background: #ffffff; | ||
border-radius: 3px; | ||
width: 18px; | ||
height: 18px; | ||
background-position: center center; | ||
background-repeat: no-repeat; | ||
|
||
&.equal { | ||
background-image: url("data:image/svg+xml,%3Csvg width='8' height='5' viewBox='0 0 8 5' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cline x1='0.5' y1='1' x2='7.5' y2='1' stroke='%23293338' stroke-linecap='round'/%3E%3Cline x1='0.5' y1='4' x2='7.5' y2='4' stroke='%23293338' stroke-linecap='round'/%3E%3C/svg%3E"); | ||
} | ||
|
||
&.not-equal { | ||
background-image: url("data:image/svg+xml,%3Csvg width='8' height='9' viewBox='0 0 8 9' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cline x1='0.5' y1='2.60288' x2='7.5' y2='2.60288' stroke='%23293338' stroke-linecap='round'/%3E%3Cline x1='0.5' y1='5.60288' x2='7.5' y2='5.60288' stroke='%23293338' stroke-linecap='round'/%3E%3Cline x1='6.18301' y1='0.785895' x2='2.18301' y2='7.7141' stroke='%23293338' stroke-linecap='round'/%3E%3C/svg%3E"); | ||
} | ||
|
||
&.selected { | ||
background-color: #e8e8e8; | ||
} | ||
} | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import CloseIcon from './close.svg'; | ||
import FilterByIcon from './filter.svg'; | ||
import OpenIcon from './open.svg'; | ||
import RemoveIcon from './remove.svg'; | ||
import SearchIcon from './search.svg'; | ||
|
||
export { CloseIcon, FilterByIcon, OpenIcon, RemoveIcon, SearchIcon }; |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.