Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add search bar component and generic response type #73

Merged
merged 5 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@devtron-labs/devtron-fe-common-lib",
"version": "0.0.53",
"version": "0.0.53-beta-3",
"description": "Supporting common component library",
"main": "dist/index.js",
"scripts": {
Expand Down
6 changes: 6 additions & 0 deletions src/Assets/Icon/ic-search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/Common/DebouncedSearch/DebouncedSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { ReactComponent as ICClear } from '../../Assets/Icon/ic-error-cross.svg'
import { DebouncedSearchProps } from './Types'

/**
* @deprecated Use `SearchBar` component instead.
*
* @param onSearch - Callback function to be called on search
* @param Icon - (Optional) Icon to be shown before the input
* @param iconClass - (Optional) Class for the icon
Expand Down
15 changes: 15 additions & 0 deletions src/Common/Helper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -559,3 +559,18 @@ export function getLockedJSON(json, jsonPathArray: string[]) {
return resultJson['$']
}

/**
* Returns a debounced variant of the function
*/
export const debounce = (func, timeout = 500) => {
let timer

return function (this, ...args) {
const context = this
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
timer = null
func.apply(context, args)
}, timeout)
}
}
85 changes: 85 additions & 0 deletions src/Common/SearchBar/SearchBar.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { ChangeEvent, FunctionComponent, useCallback, useRef, useState, KeyboardEvent } from 'react'
import { ReactComponent as Search } from '../../Assets/Icon/ic-search.svg'
import { ReactComponent as Clear } from '../../Assets/Icon/ic-error-cross.svg'
import { SearchBarProps } from './types'
import './searchBar.scss'
import { debounce, noop } from '../Helper'

/**
* Generic search input component with support for enter based and debounced search
*/
const SearchBar: FunctionComponent<SearchBarProps> = ({
initialSearchText = '',
handleSearchChange = noop,
handleEnter = noop,
inputProps = {},
containerClassName,
shouldDebounce = false,
debounceTimeout = 300,
}) => {
const [showClearButton, setShowClearButton] = useState(!!initialSearchText)
const inputRef = useRef<HTMLInputElement>()
const debouncedSearchChange = useCallback(debounce(handleSearchChange, debounceTimeout), [
handleSearchChange,
debounceTimeout,
])

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target
setShowClearButton(!!value)
if (shouldDebounce) {
debouncedSearchChange(value)
} else {
handleSearchChange(value)
}
}

const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
const { key } = e

if (key === 'Enter') {
const inputTarget = e.target as HTMLInputElement
const event = { ...e, target: { ...inputTarget, value: inputTarget.value.trim() } }
handleSearchChange(event.target.value)
handleEnter(event)
}
}

const clearSearch = () => {
inputRef.current.value = ''
handleSearchChange('')
eshankvaish marked this conversation as resolved.
Show resolved Hide resolved
}

return (
<div className={containerClassName}>
<div className="search-bar bcn-0 dc__block w-100 min-w-200 dc__position-rel en-2 bw-1 br-4 h-32">
<Search className="search-bar__icon dc__position-abs icon-color-n6 icon-dim-18" />
<input
placeholder="Search"
data-testid="search-bar"
type="text"
{...inputProps}
defaultValue={initialSearchText}
className={`search-bar__input bcn-0 dc__position-abs w-100 h-100 br-4 dc__no-border pt-6 pr-10 pb-6 pl-30 fs-13 lh-20 fw-4 cn-9 placeholder-cn5 ${
showClearButton ? 'pr-30' : 'pr-10'
}`}
onChange={handleChange}
onKeyDown={handleKeyDown}
ref={inputRef}
/>
{showClearButton && (
<button
className="flex search-bar__clear-button dc__position-abs dc__transparent mt-0 mb-0 mr-5 ml-5"
type="button"
onClick={clearSearch}
aria-label="Clear search"
>
<Clear className="icon-dim-18 icon-n4 dc__vertical-align-middle" />
</button>
)}
</div>
</div>
)
}

export default SearchBar
31 changes: 31 additions & 0 deletions src/Common/SearchBar/searchBar.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.search-bar {

&:focus,
&:focus-within {
outline: none;
border: solid 1px var(--B500);
}

&__input {

&:focus,
&:focus-visible {
border: none;
outline: none;
}
}

&__icon {
z-index: 1;
top: 50%;
left: 8px;
transform: translateY(-50%);
}

&__clear-button {
right: 0;
top: 50%;
transform: translateY(-50%);
z-index: 1;
}
}
36 changes: 36 additions & 0 deletions src/Common/SearchBar/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { InputHTMLAttributes, SyntheticEvent } from 'react'

export interface SearchBarProps {
/**
* Initial search text
*
* @default ''
*/
initialSearchText?: string
/**
* Search handler for the search input
*/
handleSearchChange?: (searchText: string) => void
/**
* Enter event handler for the search input
*/
handleEnter?: (event: SyntheticEvent) => void
/**
* Input props for the search input
*/
inputProps?: InputHTMLAttributes<HTMLInputElement>
/**
* Class name for the container; can be used for handling width
*/
containerClassName?: string
/**
* If true, the change handler would be triggered with debounce
*
* @default false
*/
shouldDebounce?: boolean
/**
* Timeout for the debounce handler to be triggered
*/
debounceTimeout?: number
}
1 change: 1 addition & 0 deletions src/Common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export * from './Common.service'
export * from './Checkbox'
export { default as EmptyState } from './EmptyState/EmptyState'
export { default as GenericEmptyState } from './EmptyState/GenericEmptyState'
export { default as SearchBar } from './SearchBar/SearchBar.component'
export { default as Toggle } from './Toggle/Toggle'
export { default as ScanVulnerabilitiesTable } from './Security/ScanVulnerabilitiesTable'
export { default as StyledRadioGroup } from './RadioGroup/RadioGroup'
Expand Down