Design InputSearch ul and handle delete user dynamic

This commit is contained in:
cyril lopez 2017-11-07 18:16:42 +01:00
parent c720955269
commit 42bbf2b852
8 changed files with 167 additions and 10 deletions

View File

@ -6,14 +6,33 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { isEmpty } from 'lodash';
import { includes, isEmpty, map, toLower } from 'lodash';
import cn from 'classnames';
import PropTypes from 'prop-types';
import InputSearchLi from 'components/InputSearchLi';
import styles from './styles.scss';
class InputSearch extends React.Component { // eslint-disable-line react/prefer-stateless-function
state = { errors: [] };
state = { errors: [], value: '', users: this.props.values, filteredUsers: this.props.values };
componentWillReceiveProps(nextProps) {
if (nextProps.didDeleteUser !== this.props.didDeleteUser) {
this.setState({ users: nextProps.values, filteredUsers: nextProps.values });
}
}
handleChange = ({ target }) => {
const filteredUsers = isEmpty(target.value) ?
this.state.users
: this.state.users.filter((user) => {
if (includes(toLower(user.name), toLower(target.value))) {
return user;
}
});
this.setState({ value: target.value, filteredUsers });
}
render() {
return (
@ -29,14 +48,21 @@ class InputSearch extends React.Component { // eslint-disable-line react/prefer-
className={cn('form-control', !isEmpty(this.state.errors) ? 'is-invalid': '')}
id={this.props.name}
name={this.props.name}
onChange={this.props.onChange}
value={this.props.value}
onChange={this.handleChange}
value={this.state.value}
placeholder={message}
type="text"
/>
)}
</FormattedMessage>
</div>
<div className={styles.ulContainer}>
<ul>
{map(this.state.filteredUsers, (user) => (
<InputSearchLi key={user.name} item={user} onClickDelete={this.props.onClickDelete} />
))}
</ul>
</div>
</div>
);
}
@ -54,6 +80,7 @@ InputSearch.proptypes = {
labelValues: PropTypes.object,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
onClickDelete: PropTypes.func.isRequired,
value: PropTypes.string,
};

View File

@ -5,6 +5,7 @@
background-color: rgba(16, 22, 34, 0.02);
border: 1px solid #E3E9F3;
border-radius: 0.25rem;
border-bottom-left-radius: 0;
color: rgba(16, 22, 34, 0.5);
line-height: 3.2rem;
font-size: 1.3rem;
@ -38,8 +39,23 @@
background-size: 0 !important;
border: 1px solid #E3E9F3;
border-radius: 0.25rem;
border-bottom-right-radius: 0;
line-height: 3.4rem;
font-size: 1.3rem;
font-family: 'Lato' !important;
}
}
.ulContainer {
height: 16.3rem;
overflow: scroll;
border: 1px solid #E3E9F3;
border-top-left-radius: 0;
border-top-right-radius: 0;
border-top: none;
border-radius: 0.25rem;
> ul {
list-style: none;
padding: 1px 0;
}
}

View File

@ -0,0 +1,37 @@
/**
*
* InputSearchLi
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import styles from './styles.scss';
function InputSearchLi({ item, onClickDelete }) {
return (
<li className={styles.li}>
<div>
<div>
{item.name}
</div>
<div onClick={() => onClickDelete(item)}>
<i className="fa fa-minus-circle" />
</div>
</div>
</li>
);
}
InputSearchLi.defaultProps = {
item: {
name: '',
}
}
InputSearchLi.proptypes = {
item: PropTypes.object,
onClickDelete: PropTypes.func.isRequired,
};
export default InputSearchLi;

View File

@ -0,0 +1,29 @@
.li {
height: 3.6rem;
margin: 0 !important;
padding: 0rem 1rem;
font-size: 13px;
font-weight: 500;
line-height: 3.6rem;
border-bottom: 1px solid #F6F6F6;
&:hover {
background-color: #FAFAFB;
background-size: 3.6rem;
> div {
>div:last-child {
color: #4B515A;
}
}
}
&:after {
content: '';
display: block;
}
> div {
display: flex;
justify-content: space-between;
> div:last-child {
color: #B3B5B9;
}
}
}

View File

@ -5,10 +5,19 @@
*/
import {
ADD_USER,
ON_CANCEL,
ON_CHANGE_INPUT,
ON_CLICK_DELETE,
} from './constants';
export function addUser(newUser) {
return {
type: ADD_USER,
newUser,
};
}
export function onCancel() {
return {
type: ON_CANCEL,
@ -22,3 +31,10 @@ export function onChangeInput({ target }) {
value: target.value,
};
}
export function onClickDelete(itemToDelete) {
return {
type: ON_CLICK_DELETE,
itemToDelete,
};
}

View File

@ -4,5 +4,7 @@
*
*/
export const ADD_USER = 'UsersPermissions/EditPage/ADD_USER';
export const ON_CANCEL = 'UsersPermissions/EditPage/ON_CANCEL';
export const ON_CHANGE_INPUT = 'UsersPermissions/EditPage/ON_CHANGE_INPUT';
export const ON_CLICK_DELETE = 'UsersPermissions/EditPage/ON_CLICK_DELETE';

View File

@ -10,7 +10,7 @@ import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { bindActionCreators, compose } from 'redux';
import { FormattedMessage } from 'react-intl';
import { get } from 'lodash';
import { get, size } from 'lodash';
import cn from 'classnames';
// Design
@ -24,8 +24,10 @@ import injectReducer from 'utils/injectReducer';
// Actions
import {
addUser,
onCancel,
onChangeInput,
onClickDelete,
} from './actions';
// Selectors
@ -61,6 +63,7 @@ export class EditPage extends React.Component { // eslint-disable-line react/pre
: 'users-permissions.EditPage.header.description';
const pluginHeaderActions = this.props.editPage.showButtons ? this.pluginHeaderActions : [];
return (
<div>
<BackHeader onClick={() => this.props.history.goBack()} />
@ -113,13 +116,15 @@ export class EditPage extends React.Component { // eslint-disable-line react/pre
</div>
</div>
<InputSearch
addUser={this.props.addUser}
didDeleteUser={this.props.editPage.didDeleteUser}
label="users-permissions.EditPage.form.roles.label.users"
labelValues={{ number: 0 }}
onChange={() => console.log('change')}
labelValues={{ number: size(get(this.props.editPage, ['modifiedData', 'users'])) }}
type="text"
validations={{ required: true }}
value=""
values={get(this.props.editPage, ['modifiedData', 'users'])}
name="users"
onClickDelete={this.props.onClickDelete}
/>
</div>
</form>
@ -133,9 +138,11 @@ export class EditPage extends React.Component { // eslint-disable-line react/pre
}
EditPage.propTypes = {
addUser: PropTypes.func.isRequired,
history: PropTypes.object.isRequired,
onCancel: PropTypes.func.isRequired,
onChangeInput: PropTypes.func.isRequired,
onClickDelete: PropTypes.func.isRequired,
};
const mapStateToProps = createStructuredSelector({
@ -145,8 +152,10 @@ const mapStateToProps = createStructuredSelector({
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
addUser,
onCancel,
onChangeInput,
onClickDelete,
},
dispatch,
);

View File

@ -6,34 +6,55 @@
import { fromJS, List, Map } from 'immutable';
import {
ADD_USER,
ON_CANCEL,
ON_CHANGE_INPUT,
ON_CLICK_DELETE,
} from './constants';
const initialState = fromJS({
didDeleteUser: false,
initialData: Map({
name: '',
description: '',
users: List([]),
users: List([
{ name: 'Pierre Burgy' },
{ name: 'Jim Laurie' },
{ name: 'Aurelien Georget' },
{ name: 'Cyril Lopez' },
]),
}),
modifiedData: Map({
name: '',
description: '',
users: List([]),
users: List([
{ name: 'Pierre Burgy' },
{ name: 'Jim Laurie' },
{ name: 'Aurelien Georget' },
{ name: 'Cyril Lopez' },
]),
}),
showButtons: false,
});
function editPageReducer(state = initialState, action) {
switch (action.type) {
case ADD_USER:
return state.updateIn(['modifiedData', 'users'], list => list.push(action.newUser));
case ON_CANCEL:
return state
.set('showButtons', false)
.set('didDeleteUser', !state.get('didDeleteUser'))
.set('modifiedData', state.get('initialData'));
case ON_CHANGE_INPUT:
return state
.set('showButtons', true)
.setIn(['modifiedData', action.key], action.value);
case ON_CLICK_DELETE:
return state
.set('didDeleteUser', !state.get('didDeleteUser'))
.set('showButtons', true)
.updateIn(['modifiedData', 'users'], list => list.filter(o => o.name !== action.itemToDelete.name));
default:
return state;
}