Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Escape button in tag edit modal #3363

Merged
merged 2 commits into from
Jan 30, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 52 additions & 30 deletions client/app/components/tags-control/TagsControl.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { map, trim } from 'lodash';
import React from 'react';
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { $uibModal } from '@/services/ng';
import TagsEditorModal from './TagsEditorModal';

export default class TagsControl extends React.Component {
static propTypes = {
Expand All @@ -15,27 +15,36 @@ export default class TagsControl extends React.Component {
static defaultProps = {
tags: [],
canEdit: false,
getAvailableTags: () => {},
getAvailableTags: () => Promise.resolve([]),
onEdit: () => {},
className: '',
};

editTags() {
const { getAvailableTags, onEdit } = this.props; // eslint-disable-line react/prop-types
const tags = map(this.props.tags, trim);
constructor(props) {
super(props);

this.state = {
showModal: false,
};

getAvailableTags().then((availableTags) => {
$uibModal
.open({
component: 'tagsEditorModal',
resolve: {
tags: () => tags,
availableTags: () => availableTags,
},
}).result.then((newTags) => {
onEdit(newTags);
});
});
// get available tags
this.props.getAvailableTags()
.then((tags) => {
this.availableTags = tags;
});
}

onTagsChanged = (newTags) => {
this.props.onEdit(newTags);
this.closeEditModal();
}

openEditModal = () => {
this.setState({ showModal: true });
}

closeEditModal = () => {
this.setState({ showModal: false });
}

// eslint-disable-next-line class-methods-use-this
Expand All @@ -55,19 +64,32 @@ export default class TagsControl extends React.Component {
}

renderEditButton() {
if (this.props.canEdit) {
return (this.props.tags.length > 0) ? (
<a className="label label-tag" role="none" onClick={() => this.editTags()}>
<i className="zmdi zmdi-edit" />
</a>
) : (
<a className="label label-tag" role="none" onClick={() => this.editTags()}>
<i className="zmdi zmdi-plus" />
Add tag
</a>
);
if (!this.props.canEdit) {
return null;
}
return null;

const tags = map(this.props.tags, trim);

const buttonLabel = tags.length > 0
? <i className="zmdi zmdi-edit" />
: <Fragment><i className="zmdi zmdi-plus" /> Add tag</Fragment>;

return (
<Fragment>
<a className="label label-tag" role="none" onClick={this.openEditModal}>
{buttonLabel}
</a>
{this.state.showModal
? <TagsEditorModal
tags={tags}
availableTags={this.availableTags}
close={this.onTagsChanged}
dismiss={this.closeEditModal}
/>
: null
}
</Fragment>
);
}

render() {
Expand Down
100 changes: 30 additions & 70 deletions client/app/components/tags-control/TagsEditorModal.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { map, trim, uniq } from 'lodash';
import { map, trim, chain } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import Select from 'antd/lib/select';
import Modal from 'antd/lib/modal';

const { Option } = Select;

class TagsEditorModal extends React.Component {
export default class TagsEditorModal extends React.Component {
static propTypes = {
tags: PropTypes.arrayOf(PropTypes.string),
availableTags: PropTypes.arrayOf(PropTypes.string),
Expand All @@ -23,81 +23,41 @@ class TagsEditorModal extends React.Component {

constructor(props) {
super(props);

this.state = {
result: uniq(map(this.props.tags, trim)),
result: chain(this.props.tags).map(trim).uniq().value(),
};
this.selectRef = React.createRef();
}

componentDidMount() {
// `autoFocus` does not work on Select because its `componentDidMount` is fired before the component
// is actually visible, so it cannot get focus. This hack should be replaced with `autoFocus` prop
// when Angular will finally gone
setTimeout(() => {
if (
this.selectRef.current &&
this.selectRef.current.rcSelect &&
this.selectRef.current.rcSelect.inputRef
) {
this.selectRef.current.rcSelect.inputRef.focus();
}
});
this.selectOptions =
chain(this.props.availableTags)
.map(trim)
.uniq()
.map(tag => <Option key={tag}>{tag}</Option>)
.value();
}

render() {
const {
availableTags,
close,
dismiss,
} = this.props;

const uniqueAvailableTags = uniq(map(availableTags, trim));
const { close, dismiss } = this.props;
const { result } = this.state;

return (
<div>
<div className="modal-header">
<button type="button" className="close" aria-hidden="true" onClick={dismiss}>&times;</button>
<h4 className="modal-title">Add/Edit Tags</h4>
</div>
<div className="modal-body">
<Select
ref={this.selectRef}
mode="tags"
className="w-100"
placeholder="Add some tags..."
defaultValue={this.state.result}
onChange={values => this.setState({ result: map(values, trim) })}
dropdownClassName="ant-dropdown-in-bootstrap-modal"
>
{uniqueAvailableTags.map(tag => (<Option key={tag}>{tag}</Option>))}
</Select>
</div>
<div className="modal-footer">
<button type="button" className="btn btn-default" onClick={dismiss}>Close</button>
<button type="button" className="btn btn-primary" onClick={() => close({ $value: this.state.result })}>Save</button>
</div>
</div>
<Modal
visible
title="Add/Edit Tags"
onOk={() => close(result)}
onCancel={dismiss}
>
<Select
mode="tags"
className="w-100"
placeholder="Add some tags..."
defaultValue={result}
onChange={values => this.setState({ result: map(values, trim) })}
autoFocus
>
{this.selectOptions}
</Select>
</Modal>
);
}
}

export default function init(ngModule) {
ngModule.component('tagsEditorModal', {
template: `
<tags-editor-modal-impl
tags="$ctrl.resolve.tags"
available-tags="$ctrl.resolve.availableTags"
close="$ctrl.close"
dismiss="$ctrl.dismiss"
></tags-editor-modal-impl>
`,
bindings: {
resolve: '<',
close: '&',
dismiss: '&',
},
});
ngModule.component('tagsEditorModalImpl', react2angular(TagsEditorModal));
}

init.init = true;