From 506b781f3a6048b433c12d25c1dbce614b5bd31b Mon Sep 17 00:00:00 2001 From: Alanna Scott Date: Thu, 17 Nov 2016 10:53:44 -0800 Subject: [PATCH] [explore-v2] add fave star and edit button to chart header (#1623) * add fave star to chart title * add TooltipWrapper, and css for icons * fix linting --- .../javascripts/components/FaveStar.jsx | 44 +++++++++++++++++++ .../javascripts/components/TooltipWrapper.jsx | 28 ++++++++++++ .../explorev2/actions/exploreActions.js | 30 +++++++++++++ .../explorev2/components/ChartContainer.jsx | 26 +++++++++++ .../components/ExploreViewContainer.jsx | 1 + .../explorev2/reducers/exploreReducer.js | 4 ++ .../javascripts/explorev2/stores/store.js | 1 + .../stylesheets/exploreV2/exploreV2.css | 5 +++ 8 files changed, 139 insertions(+) create mode 100644 superset/assets/javascripts/components/FaveStar.jsx create mode 100644 superset/assets/javascripts/components/TooltipWrapper.jsx diff --git a/superset/assets/javascripts/components/FaveStar.jsx b/superset/assets/javascripts/components/FaveStar.jsx new file mode 100644 index 0000000000000..4e6afa2883f87 --- /dev/null +++ b/superset/assets/javascripts/components/FaveStar.jsx @@ -0,0 +1,44 @@ +import React, { PropTypes } from 'react'; +import cx from 'classnames'; +import TooltipWrapper from './TooltipWrapper'; + +const propTypes = { + sliceId: PropTypes.string.isRequired, + actions: PropTypes.object.isRequired, + isStarred: PropTypes.bool.isRequired, +}; + +export default class FaveStar extends React.Component { + componentDidMount() { + this.props.actions.fetchFaveStar(this.props.sliceId); + } + + onClick(e) { + e.preventDefault(); + this.props.actions.saveFaveStar(this.props.sliceId, this.props.isStarred); + } + + render() { + const iconClassNames = cx('fa', { + 'fa-star': this.props.isStarred, + 'fa-star-o': !this.props.isStarred, + }); + + return ( + + + + + + ); + } +} + +FaveStar.propTypes = propTypes; diff --git a/superset/assets/javascripts/components/TooltipWrapper.jsx b/superset/assets/javascripts/components/TooltipWrapper.jsx new file mode 100644 index 0000000000000..56f0c148e2a25 --- /dev/null +++ b/superset/assets/javascripts/components/TooltipWrapper.jsx @@ -0,0 +1,28 @@ +import React, { PropTypes } from 'react'; +import { Tooltip, OverlayTrigger } from 'react-bootstrap'; +import { slugify } from '../modules/utils'; + +const propTypes = { + label: PropTypes.string.isRequired, + tooltip: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, + placement: PropTypes.string, +}; + +const defaultProps = { + placement: 'top', +}; + +export default function TooltipWrapper({ label, tooltip, children, placement }) { + return ( + {tooltip}} + > + {children} + + ); +} + +TooltipWrapper.propTypes = propTypes; +TooltipWrapper.defaultProps = defaultProps; diff --git a/superset/assets/javascripts/explorev2/actions/exploreActions.js b/superset/assets/javascripts/explorev2/actions/exploreActions.js index d944b839c384a..427c6bb70d8e3 100644 --- a/superset/assets/javascripts/explorev2/actions/exploreActions.js +++ b/superset/assets/javascripts/explorev2/actions/exploreActions.js @@ -1,5 +1,8 @@ /* eslint camelcase: 0 */ const $ = window.$ = require('jquery'); + +const FAVESTAR_BASE_URL = '/superset/favstar/slice'; + export const SET_FIELD_OPTIONS = 'SET_FIELD_OPTIONS'; export function setFieldOptions(options) { return { type: SET_FIELD_OPTIONS, options }; @@ -48,6 +51,33 @@ export function fetchFieldOptions(datasourceId, datasourceType) { }; } +export const TOGGLE_FAVE_STAR = 'TOGGLE_FAVE_STAR'; +export function toggleFaveStar(isStarred) { + return { type: TOGGLE_FAVE_STAR, isStarred }; +} + +export const FETCH_FAVE_STAR = 'FETCH_FAVE_STAR'; +export function fetchFaveStar(sliceId) { + return function (dispatch) { + const url = `${FAVESTAR_BASE_URL}/${sliceId}/count`; + $.get(url, (data) => { + if (data.count > 0) { + dispatch(toggleFaveStar(true)); + } + }); + }; +} + +export const SAVE_FAVE_STAR = 'SAVE_FAVE_STAR'; +export function saveFaveStar(sliceId, isStarred) { + return function (dispatch) { + const urlSuffix = isStarred ? 'unselect' : 'select'; + const url = `${FAVESTAR_BASE_URL}/${sliceId}/${urlSuffix}/`; + $.get(url); + dispatch(toggleFaveStar(!isStarred)); + }; +} + export const ADD_FILTER = 'ADD_FILTER'; export function addFilter(filter) { return { type: ADD_FILTER, filter }; diff --git a/superset/assets/javascripts/explorev2/components/ChartContainer.jsx b/superset/assets/javascripts/explorev2/components/ChartContainer.jsx index a58009477f2d0..23cb2b62dcf75 100644 --- a/superset/assets/javascripts/explorev2/components/ChartContainer.jsx +++ b/superset/assets/javascripts/explorev2/components/ChartContainer.jsx @@ -5,9 +5,13 @@ import { Panel } from 'react-bootstrap'; import visMap from '../../../visualizations/main'; import { d3format } from '../../modules/utils'; import ExploreActionButtons from '../../explore/components/ExploreActionButtons'; +import FaveStar from '../../components/FaveStar'; +import TooltipWrapper from '../../components/TooltipWrapper'; const propTypes = { + actions: PropTypes.object.isRequired, can_download: PropTypes.bool.isRequired, + slice_id: PropTypes.string.isRequired, slice_name: PropTypes.string.isRequired, viz_type: PropTypes.string.isRequired, height: PropTypes.string.isRequired, @@ -19,6 +23,7 @@ const propTypes = { column_formats: PropTypes.object, data: PropTypes.any, isChartLoading: PropTypes.bool, + isStarred: PropTypes.bool.isRequired, }; class ChartContainer extends React.Component { @@ -150,6 +155,25 @@ class ChartContainer extends React.Component { className="panel-title" > {this.props.slice_name} + + + + + + + + +
diff --git a/superset/assets/javascripts/explorev2/reducers/exploreReducer.js b/superset/assets/javascripts/explorev2/reducers/exploreReducer.js index 2bee4daba0750..e12d3172fa256 100644 --- a/superset/assets/javascripts/explorev2/reducers/exploreReducer.js +++ b/superset/assets/javascripts/explorev2/reducers/exploreReducer.js @@ -4,6 +4,10 @@ import { addToArr, removeFromArr, alterInArr } from '../../../utils/reducerUtils export const exploreReducer = function (state, action) { const actionHandlers = { + [actions.TOGGLE_FAVE_STAR]() { + return Object.assign({}, state, { isStarred: action.isStarred }); + }, + [actions.FETCH_STARTED]() { return Object.assign({}, state, { isDatasourceMetaLoading: true }); }, diff --git a/superset/assets/javascripts/explorev2/stores/store.js b/superset/assets/javascripts/explorev2/stores/store.js index 504f7029baf6d..fc07f502a7a55 100644 --- a/superset/assets/javascripts/explorev2/stores/store.js +++ b/superset/assets/javascripts/explorev2/stores/store.js @@ -1679,5 +1679,6 @@ export function initialState(vizType = 'table') { datasource_type: null, fields, viz: defaultViz(vizType), + isStarred: false, }; } diff --git a/superset/assets/stylesheets/exploreV2/exploreV2.css b/superset/assets/stylesheets/exploreV2/exploreV2.css index b46e1262ac7fa..4745c2c42d0b1 100644 --- a/superset/assets/stylesheets/exploreV2/exploreV2.css +++ b/superset/assets/stylesheets/exploreV2/exploreV2.css @@ -15,3 +15,8 @@ margin-right: 0px; margin-bottom: 100px; } + +.fave-unfave-icon, .edit-desc-icon { + padding: 0 0 0 .5em; + font-size: 14px; +}