diff --git a/client/app/components/NoTaggedObjectsFound.jsx b/client/app/components/NoTaggedObjectsFound.jsx
index a1fb102349..38ae3840fe 100644
--- a/client/app/components/NoTaggedObjectsFound.jsx
+++ b/client/app/components/NoTaggedObjectsFound.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { react2angular } from 'react2angular';
import { BigMessage } from '@/components/BigMessage';
-import TagsControl from '@/components/tags-control/TagsControl';
+import { TagsControl } from '@/components/tags-control/TagsControl';
export function NoTaggedObjectsFound({ objectType, tags }) {
return (
diff --git a/client/app/components/dashboards/AddWidgetDialog.jsx b/client/app/components/dashboards/AddWidgetDialog.jsx
index 0e3fd73870..5ffd03b93a 100644
--- a/client/app/components/dashboards/AddWidgetDialog.jsx
+++ b/client/app/components/dashboards/AddWidgetDialog.jsx
@@ -10,7 +10,7 @@ import {
ParameterMappingListInput,
editableMappingsToParameterMappings,
} from '@/components/ParameterMappingInput';
-import { QueryTagsControl } from '@/components/tags-control/QueryTagsControl';
+import { QueryTagsControl } from '@/components/tags-control/TagsControl';
import { toastr } from '@/services/ng';
import { Widget } from '@/services/widget';
diff --git a/client/app/components/tags-control/DashboardTagsControl.jsx b/client/app/components/tags-control/DashboardTagsControl.jsx
deleted file mode 100644
index 7337964308..0000000000
--- a/client/app/components/tags-control/DashboardTagsControl.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { react2angular } from 'react2angular';
-import ModelTagsControl from '@/components/tags-control/ModelTagsControl';
-
-export class DashboardTagsControl extends ModelTagsControl {
- static archivedTooltip = 'This dashboard is archived and and won\'t appear in the dashboards list or search results.';
-}
-
-export default function init(ngModule) {
- ngModule.component('dashboardTagsControl', react2angular(DashboardTagsControl));
-}
-
-init.init = true;
diff --git a/client/app/components/tags-control/ModelTagsControl.jsx b/client/app/components/tags-control/ModelTagsControl.jsx
deleted file mode 100644
index 7d8973f6e3..0000000000
--- a/client/app/components/tags-control/ModelTagsControl.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import { extend } from 'lodash';
-import React from 'react';
-import PropTypes from 'prop-types';
-import Tooltip from 'antd/lib/tooltip';
-import TagsControl from '@/components/tags-control/TagsControl';
-
-export default class ModelTagsControl extends TagsControl {
- static propTypes = extend({}, TagsControl.propTypes, {
- isDraft: PropTypes.bool,
- isArchived: PropTypes.bool,
- });
-
- static defaultProps = extend({}, TagsControl.defaultProps, {
- isDraft: false,
- isArchived: false,
- });
-
- static archivedTooltip = '';
-
- renderPrependTags() {
- const { isDraft, isArchived } = this.props;
- const result = [];
-
- if (isDraft && !isArchived) {
- result.push((
- Unpublished
- ));
- }
-
- if (isArchived) {
- result.push((
-
- Archived
-
- ));
- }
-
- return result;
- }
-}
diff --git a/client/app/components/tags-control/QueryTagsControl.jsx b/client/app/components/tags-control/QueryTagsControl.jsx
deleted file mode 100644
index 9b56c26700..0000000000
--- a/client/app/components/tags-control/QueryTagsControl.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { react2angular } from 'react2angular';
-import ModelTagsControl from '@/components/tags-control/ModelTagsControl';
-
-export class QueryTagsControl extends ModelTagsControl {
- static archivedTooltip = 'This query is archived and can\'t be used in dashboards, and won\'t appear in search results.';
-}
-
-export default function init(ngModule) {
- ngModule.component('queryTagsControl', react2angular(QueryTagsControl));
-}
-
-init.init = true;
diff --git a/client/app/components/tags-control/TagsControl.jsx b/client/app/components/tags-control/TagsControl.jsx
index 236ccd4e00..344bdef839 100644
--- a/client/app/components/tags-control/TagsControl.jsx
+++ b/client/app/components/tags-control/TagsControl.jsx
@@ -1,15 +1,18 @@
-import { map, trim } from 'lodash';
-import React, { Fragment } from 'react';
+import { map, trim, extend } from 'lodash';
+import React from 'react';
import PropTypes from 'prop-types';
+import { react2angular } from 'react2angular';
+import Tooltip from 'antd/lib/tooltip';
import TagsEditorModal from './TagsEditorModal';
-export default class TagsControl extends React.Component {
+export class TagsControl extends React.Component {
static propTypes = {
tags: PropTypes.arrayOf(PropTypes.string),
canEdit: PropTypes.bool,
getAvailableTags: PropTypes.func,
onEdit: PropTypes.func,
className: PropTypes.string,
+ children: PropTypes.node,
};
static defaultProps = {
@@ -18,87 +21,103 @@ export default class TagsControl extends React.Component {
getAvailableTags: () => Promise.resolve([]),
onEdit: () => {},
className: '',
+ children: null,
};
- constructor(props) {
- super(props);
-
- this.state = {
- showModal: false,
- };
-
- // get available tags
- this.props.getAvailableTags()
- .then((tags) => {
- this.availableTags = tags;
- });
- }
-
- onTagsChanged = (newTags) => {
- this.props.onEdit(newTags);
- this.closeEditModal();
- }
+ state = {
+ showModal: false,
+ };
openEditModal = () => {
this.setState({ showModal: true });
- }
+ };
closeEditModal = () => {
this.setState({ showModal: false });
- }
-
- // eslint-disable-next-line class-methods-use-this
- renderPrependTags() {
- return null;
- }
-
- renderTags() {
- return map(this.props.tags, tag => (
- {tag}
- ));
- }
+ };
- // eslint-disable-next-line class-methods-use-this
- renderAppendTags() {
- return null;
- }
+ onTagsChanged = (newTags) => {
+ this.props.onEdit(newTags);
+ this.closeEditModal();
+ };
renderEditButton() {
- if (!this.props.canEdit) {
- return null;
- }
-
const tags = map(this.props.tags, trim);
-
const buttonLabel = tags.length > 0
?
- : Add tag;
+ : Add tag;
return (
-
-
- {buttonLabel}
-
+
+ {buttonLabel}
{this.state.showModal && (
)}
-
+
);
}
render() {
return (
- {this.renderPrependTags()}
- {this.renderTags()}
- {this.renderAppendTags()}
- {this.renderEditButton()}
+ {this.props.children}
+ {map(this.props.tags, tag => (
+ {tag}
+ ))}
+ {this.props.canEdit && this.renderEditButton()}
);
}
}
+
+function modelTagsControl({ archivedTooltip }) {
+ // See comment for `propTypes`/`defaultProps`
+ // eslint-disable-next-line react/prop-types
+ function ModelTagsControl({ isDraft, isArchived, ...props }) {
+ return (
+
+ {!isArchived && isDraft && (
+ Unpublished
+ )}
+ {isArchived && (
+
+ Archived
+
+ )}
+
+ );
+ }
+
+ // ANGULAR_REMOVE_ME `extend` needed just for `react2angular`, so remove it when `react2angular` no longer needed
+ ModelTagsControl.propTypes = extend({
+ isDraft: PropTypes.bool,
+ isArchived: PropTypes.bool,
+ }, TagsControl.propTypes);
+
+ ModelTagsControl.defaultProps = extend({
+ isDraft: false,
+ isArchived: false,
+ }, TagsControl.defaultProps);
+
+ return ModelTagsControl;
+}
+
+export const QueryTagsControl = modelTagsControl({
+ archivedTooltip: 'This query is archived and can\'t be used in dashboards, or appear in search results.',
+});
+
+export const DashboardTagsControl = modelTagsControl({
+ archivedTooltip: 'This dashboard is archived and won\'t be listed in dashboards nor search results.',
+});
+
+export default function init(ngModule) {
+ ngModule.component('queryTagsControl', react2angular(QueryTagsControl));
+ ngModule.component('dashboardTagsControl', react2angular(DashboardTagsControl));
+}
+
+init.init = true;
diff --git a/client/app/components/tags-control/TagsEditorModal.jsx b/client/app/components/tags-control/TagsEditorModal.jsx
index 0ed0fc6ffe..a35410886c 100644
--- a/client/app/components/tags-control/TagsEditorModal.jsx
+++ b/client/app/components/tags-control/TagsEditorModal.jsx
@@ -1,61 +1,78 @@
-import { map, trim, chain } from 'lodash';
+import { map, trim, uniq } from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import Select from 'antd/lib/select';
import Modal from 'antd/lib/modal';
-const { Option } = Select;
-
export default class TagsEditorModal extends React.Component {
static propTypes = {
tags: PropTypes.arrayOf(PropTypes.string),
- availableTags: PropTypes.arrayOf(PropTypes.string),
- close: PropTypes.func,
- dismiss: PropTypes.func,
+ getAvailableTags: PropTypes.func.isRequired,
+ onConfirm: PropTypes.func,
+ onCancel: PropTypes.func,
};
static defaultProps = {
tags: [],
- availableTags: [],
- close: () => {},
- dismiss: () => {},
+ onConfirm: () => {},
+ onCancel: () => {},
};
constructor(props) {
super(props);
this.state = {
- result: chain(this.props.tags).map(trim).uniq().value(),
+ isVisible: true,
+ loading: true,
+ availableTags: [],
+ result: uniq(map(this.props.tags, trim)),
+ onAfterClose: () => {},
};
+ }
- this.selectOptions =
- chain(this.props.availableTags)
- .map(trim)
- .uniq()
- .map(tag => )
- .value();
+ componentDidMount() {
+ this.props.getAvailableTags().then((availableTags) => {
+ this.setState({
+ loading: false,
+ availableTags: uniq(map(availableTags, trim)),
+ });
+ });
}
- render() {
- const { close, dismiss } = this.props;
- const { result } = this.state;
+ onConfirm(result) {
+ this.setState({
+ isVisible: false,
+ onAfterClose: () => this.props.onConfirm(result),
+ });
+ }
+ onCancel() {
+ this.setState({
+ isVisible: false,
+ onAfterClose: () => this.props.onCancel(),
+ });
+ }
+
+ render() {
return (
close(result)}
- onCancel={dismiss}
+ onOk={() => this.onConfirm(this.state.result)}
+ onCancel={() => this.onCancel()}
+ afterClose={() => this.state.onAfterClose()}
>
);
diff --git a/client/app/pages/dashboards/DashboardList.jsx b/client/app/pages/dashboards/DashboardList.jsx
index 1478b03c7b..212799f23c 100644
--- a/client/app/pages/dashboards/DashboardList.jsx
+++ b/client/app/pages/dashboards/DashboardList.jsx
@@ -3,7 +3,7 @@ import { react2angular } from 'react2angular';
import { PageHeader } from '@/components/PageHeader';
import { Paginator } from '@/components/Paginator';
-import { DashboardTagsControl } from '@/components/tags-control/DashboardTagsControl';
+import { DashboardTagsControl } from '@/components/tags-control/TagsControl';
import { wrap as liveItemsList, createResourceFetcher, ControllerType } from '@/components/items-list/LiveItemsList';
import LoadingState from '@/components/items-list/components/LoadingState';
diff --git a/client/app/pages/queries-list/QueriesList.jsx b/client/app/pages/queries-list/QueriesList.jsx
index 2789d942ed..cc81cc589c 100644
--- a/client/app/pages/queries-list/QueriesList.jsx
+++ b/client/app/pages/queries-list/QueriesList.jsx
@@ -3,7 +3,7 @@ import { react2angular } from 'react2angular';
import { PageHeader } from '@/components/PageHeader';
import { Paginator } from '@/components/Paginator';
-import { QueryTagsControl } from '@/components/tags-control/QueryTagsControl';
+import { QueryTagsControl } from '@/components/tags-control/TagsControl';
import { SchedulePhrase } from '@/components/queries/SchedulePhrase';
import { wrap as liveItemsList, createResourceFetcher, ControllerType } from '@/components/items-list/LiveItemsList';