in addons/addon-base-ui/packages/base-ui/src/parts/users/UsersList.js [126:325]
renderUsers() {
// Read "this.mapOfUsersBeingEdited" in the "render" method here
// The usersBeingEditedMap is then used in the ReactTable
// If we directly use this.mapOfUsersBeingEdited in the ReactTable's cell method, MobX does not
// realize that it is being used in the outer component's "render" method's scope
// Due to this, MobX does not re-render the component when observable state changes.
// To make this work correctly, we need to access "this.mapOfUsersBeingEdited" out side of ReactTable once
const usersBeingEditedMap = this.mapOfUsersBeingEdited;
const store = this.getStore();
const nonRootUsers = store.nonRootUsers;
// const nonRootUsers = store.list;
const pageSize = Math.min(nonRootUsers.length, 50);
const showPagination = nonRootUsers.length > pageSize;
const displayEditableInput = attributeName => row => {
const user = row.original;
const userBeingEdited = usersBeingEditedMap[user.id];
const handleChange = action(event => {
event.preventDefault();
userBeingEdited[attributeName] = event.target.value;
});
return userBeingEdited ? (
<div className="ui focus input">
<input type="text" defaultValue={row.value} onChange={handleChange} />
</div>
) : (
user[attributeName]
);
};
const handleCheckboxChange = (userBeingEdited, attributeName) =>
action((event, { checked }) => {
userBeingEdited[attributeName] = checked;
// update this.mapOfUsersBeingEdited reference to force re-render
this.mapOfUsersBeingEdited = _.clone(this.mapOfUsersBeingEdited);
event.stopPropagation();
});
const handleRadioChange = (userBeingEdited, attributeName) =>
action((event, { value }) => {
userBeingEdited[attributeName] = value;
// update this.mapOfUsersBeingEdited reference to force re-render
this.mapOfUsersBeingEdited = _.clone(this.mapOfUsersBeingEdited);
event.stopPropagation();
});
const booleanColumnValueFilter = (trueString = 'yes', falseString = 'no') => (filter, row) => {
const columnValueBoolean = row[filter.id];
const columnValueStr = columnValueBoolean ? trueString : falseString;
const filterValue = filter.value.toLowerCase();
// Allow filtering by typing "yes/no" or "true/false"
return (
columnValueStr.indexOf(filterValue) === 0 ||
String(columnValueBoolean)
.toLowerCase()
.indexOf(filterValue) === 0
);
};
const processing = this.formProcessing;
if (!store.hasNonRootUsers) {
return null;
}
return (
// TODO: add api token stats and active flag here in the table
<Segment basic className="p0">
<Dimmer active={processing} inverted>
<Loader inverted>Updating</Loader>
</Dimmer>
<ReactTable
data={nonRootUsers}
defaultSorted={[{ id: 'lastName', desc: true }]}
showPagination={showPagination}
defaultPageSize={pageSize}
className="-striped -highlight"
filterable
defaultFilterMethod={(filter, row) => {
const columnValue = String(row[filter.id]).toLowerCase();
const filterValue = filter.value.toLowerCase();
return columnValue.indexOf(filterValue) >= 0;
}}
columns={[
{
Header: 'User Name',
accessor: 'username',
},
{
Header: 'Email',
accessor: 'email',
Cell: displayEditableInput('email'),
},
{
Header: 'First Name',
accessor: 'firstName',
Cell: displayEditableInput('firstName'),
},
{
Header: 'Last Name',
accessor: 'lastName',
Cell: displayEditableInput('lastName'),
},
{
Header: 'Admin',
accessor: 'isAdmin',
filterMethod: booleanColumnValueFilter(),
Cell: row => {
const user = row.original;
const userBeingEdited = usersBeingEditedMap[user.id];
return userBeingEdited ? (
<span>
<Checkbox
checked={userBeingEdited.isAdmin}
label={userBeingEdited.isAdmin ? 'Yes' : 'No'}
onChange={handleCheckboxChange(userBeingEdited, 'isAdmin')}
/>
</span>
) : user.isAdmin ? (
<span>
<i className="check circle outline icon green" />
Yes
</span>
) : (
<span>No</span>
);
},
},
{
Header: 'Status',
accessor: 'isActive',
filterMethod: booleanColumnValueFilter('active', 'inactive'),
minWidth: 125,
Cell: row => {
const user = row.original;
const userBeingEdited = usersBeingEditedMap[user.id];
const isActive = userBeingEdited ? userBeingEdited.status.toLowerCase() === 'active' : row.value;
return userBeingEdited ? (
<span>
<Radio
name={`status-${user.id}`}
checked={isActive}
value="active"
label="Active"
onChange={handleRadioChange(userBeingEdited, 'status')}
/>
<Radio
className="ml1"
name={`status-${user.id}`}
checked={!isActive}
value="inactive"
label="Inactive"
onChange={handleRadioChange(userBeingEdited, 'status')}
/>
</span>
) : user.isActive ? (
<span>
<Label color="green">
<i className="check circle outline icon" />
Active
</Label>
</span>
) : (
<span>
<Label color="red">Inactive</Label>
</span>
);
},
},
{
Header: '',
filterable: false,
Cell: cell => {
const user = cell.original;
const userBeingEdited = usersBeingEditedMap[user.id];
return userBeingEdited ? (
<span>
<Icon
name="checkmark"
className="ml1 cursor-pointer"
color="green"
onClick={this.handleSave(userBeingEdited)}
/>
<Icon
name="close"
className="ml1 cursor-pointer"
color="red"
onClick={this.handleCancel(userBeingEdited)}
/>
</span>
) : (
<Icon name="pencil" className="ml1 cursor-pointer" color="blue" onClick={this.handleEditorOn(user)} />
);
},
},
]}
/>
</Segment>
);
}