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';