Skip to content

Commit

Permalink
Alert redesign #3 - Destinations single pane mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ranbena committed Sep 22, 2019
1 parent 0917d56 commit 6541a05
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 39 deletions.
67 changes: 42 additions & 25 deletions client/app/components/SelectItemsDialog.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { filter, debounce, find } from 'lodash';
import { filter, debounce, find, isEmpty, size } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Modal from 'antd/lib/modal';
import Input from 'antd/lib/input';
import List from 'antd/lib/list';
import Button from 'antd/lib/button';
import { wrap as wrapDialog, DialogPropType } from '@/components/DialogWrapper';
import { BigMessage } from '@/components/BigMessage';

Expand All @@ -29,6 +30,9 @@ class SelectItemsDialog extends React.Component {
// right list; args/results save as for `renderItem`. if not specified - `renderItem` will be used
renderStagedItem: PropTypes.func,
save: PropTypes.func, // (selectedItems[]) => Promise<any>
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
extraFooterContent: PropTypes.node,
showCount: PropTypes.bool,
};

static defaultProps = {
Expand All @@ -37,8 +41,11 @@ class SelectItemsDialog extends React.Component {
selectedItemsTitle: 'Selected items',
itemKey: item => item.id,
renderItem: () => '',
renderStagedItem: null, // use `renderItem` by default
renderStagedItem: null, // hidden by default
save: items => items,
width: '80%',
extraFooterContent: null,
showCount: false,
};

state = {
Expand Down Expand Up @@ -108,7 +115,7 @@ class SelectItemsDialog extends React.Component {
renderItem(item, isStagedList) {
const { renderItem, renderStagedItem } = this.props;
const isSelected = this.isSelected(item);
const render = isStagedList ? (renderStagedItem || renderItem) : renderItem;
const render = isStagedList ? renderStagedItem : renderItem;

const { content, className, isDisabled } = render(item, { isSelected });

Expand All @@ -123,37 +130,45 @@ class SelectItemsDialog extends React.Component {
}

render() {
const { dialog, dialogTitle, inputPlaceholder, selectedItemsTitle } = this.props;
const { dialog, dialogTitle, inputPlaceholder } = this.props;
const { selectedItemsTitle, renderStagedItem, width, showCount } = this.props;
const { loading, saveInProgress, items, selected } = this.state;
const hasResults = items.length > 0;
return (
<Modal
{...dialog.props}
width="80%"
className="select-items-dialog"
width={width}
title={dialogTitle}
okText="Save"
okButtonProps={{
loading: saveInProgress,
disabled: selected.length === 0,
}}
onOk={() => this.save()}
footer={(
<div className="d-flex align-items-center">
<span className="flex-fill m-r-5" style={{ textAlign: 'left', color: 'rgba(0, 0, 0, 0.5)' }}>{this.props.extraFooterContent}</span>
<Button onClick={dialog.dismiss}>Cancel</Button>
<Button onClick={() => this.save()} loading={saveInProgress} disabled={selected.length === 0} type="primary">
Save
{showCount && !isEmpty(selected) ? ` (${size(selected)})` : null}
</Button>
</div>
)}
>
<div className="d-flex align-items-center m-b-10">
<div className="w-50 m-r-10">
<div className="flex-fill">
<Input.Search
defaultValue={this.state.searchTerm}
onChange={event => this.search(event.target.value)}
placeholder={inputPlaceholder}
autoFocus
/>
</div>
<div className="w-50 m-l-10">
<h5 className="m-0">{selectedItemsTitle}</h5>
</div>
{renderStagedItem && (
<div className="w-50 m-l-20">
<h5 className="m-0">{selectedItemsTitle}</h5>
</div>
)}
</div>

<div className="d-flex align-items-stretch" style={{ minHeight: '30vh', maxHeight: '50vh' }}>
<div className="w-50 m-r-10 scrollbox">
<div className="flex-fill scrollbox">
{loading && <LoadingState className="" />}
{!loading && !hasResults && (
<BigMessage icon="fa-search" message="No items match your search." className="" />
Expand All @@ -166,15 +181,17 @@ class SelectItemsDialog extends React.Component {
/>
)}
</div>
<div className="w-50 m-l-10 scrollbox">
{(selected.length > 0) && (
<List
size="small"
dataSource={selected}
renderItem={item => this.renderItem(item, true)}
/>
)}
</div>
{renderStagedItem && (
<div className="w-50 m-l-20 scrollbox">
{(selected.length > 0) && (
<List
size="small"
dataSource={selected}
renderItem={item => this.renderItem(item, true)}
/>
)}
</div>
)}
</div>
</Modal>
);
Expand Down
8 changes: 5 additions & 3 deletions client/app/components/groups/ListItemAddon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,26 @@ import React from 'react';
import PropTypes from 'prop-types';
import Tooltip from 'antd/lib/tooltip';

export default function ListItemAddon({ isSelected, isStaged, alreadyInGroup }) {
export default function ListItemAddon({ isSelected, isStaged, alreadyInGroup, deselectedIcon }) {
if (isStaged) {
return <i className="fa fa-remove" />;
}
if (alreadyInGroup) {
return <Tooltip title="Already in this group"><i className="fa fa-check" /></Tooltip>;
return <Tooltip title="Already selected"><i className="fa fa-check" /></Tooltip>;
}
return isSelected ? <i className="fa fa-check" /> : <i className="fa fa-angle-double-right" />;
return isSelected ? <i className="fa fa-check" /> : <i className={`fa ${deselectedIcon}`} />;
}

ListItemAddon.propTypes = {
isSelected: PropTypes.bool,
isStaged: PropTypes.bool,
alreadyInGroup: PropTypes.bool,
deselectedIcon: PropTypes.string,
};

ListItemAddon.defaultProps = {
isSelected: false,
isStaged: false,
alreadyInGroup: false,
deselectedIcon: 'fa-angle-double-right',
};
23 changes: 12 additions & 11 deletions client/app/pages/alert/components/AlertDestinations.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,18 @@ export default class AlertDestinations extends React.Component {
const { dests, subs } = this.state;

SelectItemsDialog.showModal({
width: 570,
showCount: true,
extraFooterContent: (
<>
<i className="fa fa-info-circle" />{' '}
Create new destinations in{' '}
<Tooltip title="Opens page in a new tab.">
<a href="/destinations/new" target="_blank">Alert Destinations</a>
</Tooltip>
</>
),
dialogTitle: 'Add Existing Alert Destinations',
selectedItemsTitle: 'Pending Destinations',
inputPlaceholder: 'Search destinations...',
searchItems: (searchTerm) => {
searchTerm = searchTerm.toLowerCase();
Expand All @@ -101,22 +111,13 @@ export default class AlertDestinations extends React.Component {
<div className="destination-wrapper">
<img src={`${IMG_ROOT}/${item.type}.png`} className="destination-icon" alt={name} />
<span className="flex-fill">{item.name}</span>
<ListItemAddon isSelected={isSelected} alreadyInGroup={alreadyInGroup} />
<ListItemAddon isSelected={isSelected} alreadyInGroup={alreadyInGroup} deselectedIcon="fa-plus" />
</div>
),
isDisabled: alreadyInGroup,
className: isSelected || alreadyInGroup ? 'selected' : '',
};
},
renderStagedItem: item => ({
content: (
<div className="destination-wrapper">
<img src={`${IMG_ROOT}/${item.type}.png`} className="destination-icon" alt={name} />
<span className="flex-fill">{item.name}</span>
<ListItemAddon isStaged />
</div>
),
}),
save: (items) => {
const promises = map(items, item => this.subscribe(item));
return Promise.all(promises).then(() => {
Expand Down
9 changes: 9 additions & 0 deletions client/app/pages/alert/components/AlertDestinations.less
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
min-height: 38px;
width: 100%;

.select-items-dialog & {
padding: 0;
}

.destination-icon {
height: 25px;
width: 25px;
Expand All @@ -58,5 +62,10 @@
justify-content: center;
font-size: 12px;
}

.select-items-dialog & {
width: 35px;
height: 35px;
}
}
}

0 comments on commit 6541a05

Please sign in to comment.