1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-05-26 01:17:00 +02:00

feat: add search for applications

This commit is contained in:
Ivar Conradi Østhus 2020-09-24 19:31:49 +02:00
parent d88435f0c5
commit 130110f5a4
8 changed files with 144 additions and 48 deletions

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { ProgressBar, Card, CardText, Icon } from 'react-mdl';
import { AppsLinkList, styles as commonStyles } from '../common';
import SearchField from '../common/search-field';
const Empty = () => (
<React.Fragment>
@ -22,6 +23,8 @@ class ClientStrategies extends Component {
static propTypes = {
applications: PropTypes.array,
fetchAll: PropTypes.func.isRequired,
settings: PropTypes.object.isRequired,
updateSetting: PropTypes.func.isRequired,
};
componentDidMount() {
@ -35,9 +38,17 @@ class ClientStrategies extends Component {
return <ProgressBar indeterminate />;
}
return (
<Card shadow={0} className={commonStyles.fullwidth}>
{applications.length > 0 ? <AppsLinkList apps={applications} /> : <Empty />}
</Card>
<div>
<div className={commonStyles.toolbar}>
<SearchField
value={this.props.settings.filter}
updateValue={this.props.updateSetting.bind(this, 'filter')}
/>
</div>
<Card shadow={0} className={commonStyles.fullwidth}>
{applications.length > 0 ? <AppsLinkList apps={applications} /> : <Empty />}
</Card>
</div>
);
}
}

View File

@ -1,9 +1,21 @@
import { connect } from 'react-redux';
import ApplicationList from './application-list-component';
import { fetchAll } from './../../store/application/actions';
import { updateSettingForGroup } from '../../store/settings/actions';
const mapStateToProps = state => ({ applications: state.applications.get('list').toJS() });
const mapStateToProps = state => {
const applications = state.applications.get('list').toJS();
const settings = state.settings.toJS().application || {};
const Container = connect(mapStateToProps, { fetchAll })(ApplicationList);
const regex = new RegExp(settings.filter, 'i');
return {
applications: settings.filter ? applications.filter(a => regex.test(a.appName)) : applications,
settings,
};
};
const mapDispatchToProps = { fetchAll, updateSetting: updateSettingForGroup('application') };
const Container = connect(mapStateToProps, mapDispatchToProps)(ApplicationList);
export default Container;

View File

@ -85,4 +85,17 @@
.toggleName {
color: #37474f !important;
font-weight: 500;
}
.toolbar {
position: relative;
padding: 0 24px 16px 24px;
text-align: center;
}
.toolbarButton {
position: absolute;
top: 56px;
right: 24px;
z-index: 2;
}

View File

@ -11,14 +11,14 @@ export const shorten = (str, len = 50) => (str && str.length > len ? `${str.subs
export const AppsLinkList = ({ apps }) => (
<List>
{apps.length > 0 &&
apps.map(({ appName, description = '-', icon }) => (
apps.map(({ appName, description, icon }) => (
<ListItem twoLine key={appName}>
<span className="mdl-list__item-primary-content" style={{ minWidth: 0 }}>
<Icon name={icon || 'apps'} className="mdl-list__item-avatar" />
<Link to={`/applications/${appName}`} className={[styles.listLink, styles.truncate].join(' ')}>
{appName}
<span className={['mdl-list__item-sub-title', styles.truncate].join(' ')}>
{description}
{description || 'No descriptionn'}
</span>
</Link>
</span>

View File

@ -0,0 +1,50 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { debounce } from 'debounce';
import { FABButton, Icon, Textfield } from 'react-mdl';
function SearchField({ value, updateValue }) {
const [localValue, setLocalValue] = useState(value);
const debounceUpdateValue = debounce(updateValue, 500);
const handleCange = e => {
e.preventDefault();
const v = e.target.value || '';
setLocalValue(v);
debounceUpdateValue(v);
};
const handleKeyPress = e => {
if (e.key === 'Enter') {
updateValue(localValue);
}
};
const updateNow = () => {
updateValue(localValue);
};
return (
<div>
<Textfield
floatingLabel
value={localValue}
onChange={handleCange}
onBlur={updateNow}
onKeyPress={handleKeyPress}
label="Search"
style={{ width: '500px', maxWidth: '80%' }}
/>
<FABButton mini className={'mdl-cell--hide-phone'}>
<Icon name="search" />
</FABButton>
</div>
);
}
SearchField.propTypes = {
value: PropTypes.string.isRequired,
updateValue: PropTypes.func.isRequired,
};
export default SearchField;

View File

@ -5,16 +5,29 @@ exports[`renders correctly with one feature 1`] = `
<div
className="toolbar"
>
<react-mdl-Textfield
floatingLabel={true}
label="Search"
onChange={[Function]}
style={
Object {
"width": "100%",
<div>
<react-mdl-Textfield
floatingLabel={true}
label="Search"
onBlur={[Function]}
onChange={[Function]}
onKeyPress={[Function]}
style={
Object {
"maxWidth": "80%",
"width": "500px",
}
}
}
/>
/>
<react-mdl-FABButton
className="mdl-cell--hide-phone"
mini={true}
>
<react-mdl-Icon
name="search"
/>
</react-mdl-FABButton>
</div>
<a
className="toolbarButton"
href="/features/create"
@ -190,16 +203,29 @@ exports[`renders correctly with one feature without permissions 1`] = `
<div
className="toolbar"
>
<react-mdl-Textfield
floatingLabel={true}
label="Search"
onChange={[Function]}
style={
Object {
"width": "100%",
<div>
<react-mdl-Textfield
floatingLabel={true}
label="Search"
onBlur={[Function]}
onChange={[Function]}
onKeyPress={[Function]}
style={
Object {
"maxWidth": "80%",
"width": "500px",
}
}
}
/>
/>
<react-mdl-FABButton
className="mdl-cell--hide-phone"
mini={true}
>
<react-mdl-Icon
name="search"
/>
</react-mdl-FABButton>
</div>
</div>
<react-mdl-Card

View File

@ -1,15 +1,3 @@
.toolbar {
position: relative;
padding: 0 104px 16px 24px;
}
.toolbarButton {
position: absolute;
top: 56px;
right: 24px;
z-index: 2;
}
.listItemMetric {
width: 40px;
flex-shrink: 0;

View File

@ -2,9 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import { debounce } from 'debounce';
import { Link } from 'react-router-dom';
import { Icon, FABButton, Textfield, Menu, MenuItem, Card, CardActions, List } from 'react-mdl';
import { Icon, FABButton, Menu, MenuItem, Card, CardActions, List } from 'react-mdl';
import Feature from './feature-list-item-component';
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
import SearchField from '../common/search-field';
import styles from './feature.scss';
import { CREATE_FEATURE } from '../../permissions';
@ -59,18 +60,13 @@ export default class FeatureListComponent extends React.Component {
});
return (
<div>
<div className={styles.toolbar}>
<Textfield
floatingLabel
value={this.state.filter}
onChange={e => {
this.setFilter(e.target.value);
}}
label="Search"
style={{ width: '100%' }}
<div className={commonStyles.toolbar}>
<SearchField
value={this.props.settings.filter}
updateValue={this.props.updateSetting.bind(this, 'filter')}
/>
{hasPermission(CREATE_FEATURE) ? (
<Link to="/features/create" className={styles.toolbarButton}>
<Link to="/features/create" className={commonStyles.toolbarButton}>
<FABButton accent title="Create feature toggle">
<Icon name="add" />
</FABButton>