-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
- Loading branch information
There are no files selected for viewing
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,126 @@ | ||
import { isFunction, isString, defaultTo, filter, map } from 'lodash'; | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import SearchInput from './SearchInput'; | ||
import SidebarMenu from './SidebarMenu'; | ||
import SidebarTags from './SidebarTags'; | ||
import PageSizeSelect from './PageSizeSelect'; | ||
|
||
export default function Sidebar({ menuItems, selectedItem, searchPlaceholder, tagsUrl }) { | ||
return ( | ||
<React.Fragment> | ||
<SearchInput placeholder={searchPlaceholder} /> | ||
<SidebarMenu items={menuItems} selected={selectedItem} /> | ||
<SidebarTags url={tagsUrl} /> | ||
<PageSizeSelect /> | ||
</React.Fragment> | ||
); | ||
} | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
kravets-levko
Author
Collaborator
|
||
import classNames from 'classnames'; | ||
import Input from 'antd/lib/input'; | ||
import Select from 'antd/lib/select'; | ||
|
||
import { TagsList } from '@/components/TagsList'; | ||
|
||
import { clientConfig } from '@/services/auth'; | ||
|
||
import ItemsListContext from '../ItemsListContext'; | ||
|
||
export default class Sidebar extends React.Component { | ||
static propTypes = { | ||
searchPlaceholder: PropTypes.string, | ||
menuItems: PropTypes.arrayOf(PropTypes.shape({ | ||
key: PropTypes.string.isRequired, | ||
href: PropTypes.string.isRequired, | ||
// string: CSS class to use as icon (with `<i> tag) | ||
// function: return value will be used as icon | ||
icon: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), | ||
This comment has been minimized.
Sorry, something went wrong.
ranbena
Contributor
|
||
title: PropTypes.string.isRequired, | ||
// boolean: `true` to show item, `false` to hide | ||
// function: should return boolean | ||
// if omitted: show item | ||
isAvailable: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]), | ||
})), | ||
selectedItem: PropTypes.string, | ||
tagsUrl: PropTypes.string, | ||
pageSizeOptions: PropTypes.arrayOf(PropTypes.number), | ||
}; | ||
|
||
static defaultProps = { | ||
searchPlaceholder: 'Search...', | ||
menuItems: [], | ||
selectedItem: PropTypes.string, | ||
tagsUrl: '', | ||
pageSizeOptions: null, // defaults to `clientConfig.pageSizeOptions`, but it's not available on this stage | ||
}; | ||
|
||
Sidebar.propTypes = { | ||
menuItems: PropTypes.array, // eslint-disable-line react/forbid-prop-types | ||
selectedItem: PropTypes.string, | ||
searchPlaceholder: PropTypes.string, | ||
tagsUrl: PropTypes.string, | ||
}; | ||
|
||
Sidebar.defaultProps = { | ||
menuItems: [], | ||
selectedItem: PropTypes.string, | ||
searchPlaceholder: 'Search...', | ||
tagsUrl: '', | ||
}; | ||
static contextType = ItemsListContext; | ||
|
||
renderSearchInput() { | ||
return ( | ||
<div className="m-b-10"> | ||
<Input | ||
className="form-control" | ||
placeholder={this.props.searchPlaceholder} | ||
defaultValue={this.context.searchTerm} | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
ranbena
Contributor
|
||
onChange={event => this.context.updateSearch(event.target.value)} | ||
autoFocus | ||
/> | ||
</div> | ||
); | ||
} | ||
|
||
renderMenu() { | ||
const items = filter( | ||
this.props.menuItems, | ||
item => (isFunction(item.isAvailable) ? item.isAvailable() : defaultTo(item.isAvailable, true)), | ||
); | ||
if (items.length === 0) { | ||
return null; | ||
} | ||
return ( | ||
<div className="list-group m-b-10 tags-list tiled"> | ||
{map(items, item => ( | ||
<a | ||
key={item.key} | ||
href={item.href} | ||
className={classNames('list-group-item', { active: this.props.selectedItem === item.key })} | ||
> | ||
{ | ||
isString(item.icon) && (item.icon !== '') && | ||
<span className="btn-favourite m-r-5"><i className={item.icon} aria-hidden="true" /></span> | ||
} | ||
{isFunction(item.icon) && (item.icon(item) || null)} | ||
{item.title} | ||
</a> | ||
))} | ||
</div> | ||
); | ||
} | ||
|
||
renderTags() { | ||
if (this.props.tagsUrl === '') { | ||
return null; | ||
} | ||
return ( | ||
<div className="m-b-10"> | ||
<TagsList tagsUrl={this.props.tagsUrl} onUpdate={tags => this.context.updateSelectedTags(tags)} /> | ||
</div> | ||
); | ||
} | ||
|
||
renderPageSizeSelect() { | ||
const items = this.props.pageSizeOptions || clientConfig.pageSizeOptions; | ||
return ( | ||
<div className="m-b-10"> | ||
<div className="m-b-10"> | ||
<Select | ||
className="w-100" | ||
defaultValue={this.context.itemsPerPage} | ||
onChange={pageSize => this.context.updatePaginator({ pageSize })} | ||
> | ||
{map(items, option => ( | ||
<Select.Option key={option} value={option}>{ option } results</Select.Option> | ||
))} | ||
</Select> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
render() { | ||
return ( | ||
<React.Fragment> | ||
{this.renderSearchInput()} | ||
{this.renderMenu()} | ||
{this.renderTags()} | ||
{this.renderPageSizeSelect()} | ||
</React.Fragment> | ||
); | ||
} | ||
} |
This file was deleted.
This file was deleted.
@kravets-levko the code LGTM, but I'm unsure of the reasoning - wouldn't we benefit from keeping
<SearchInput
> an independent component?