You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by el...@apache.org on 2021/07/27 22:10:48 UTC
[superset] branch selfSubscribeReports updated: feat: add active
email toggle and dropdown on dashboard (#15852)
This is an automated email from the ASF dual-hosted git repository.
elizabeth pushed a commit to branch selfSubscribeReports
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/selfSubscribeReports by this push:
new 4d5f17d feat: add active email toggle and dropdown on dashboard (#15852)
4d5f17d is described below
commit 4d5f17d6e1af382f048114d0247b19dfdc946eaf
Author: AAfghahi <48...@users.noreply.github.com>
AuthorDate: Tue Jul 27 18:09:48 2021 -0400
feat: add active email toggle and dropdown on dashboard (#15852)
* first draft
* added testing props
* created Api call
* added click logic
* made the fetch report into a action/reducer
* abstracted report action
* first commit
* toggle started
* on blue
* event listener
* toggle
* toggle active + dropdown
* fixed cypress tests
* revisions
* removed useState
* added toggle active to state
---
superset-frontend/src/common/components/index.tsx | 8 +-
.../HeaderReportActionsDropdown/index.tsx | 99 ++++++++++++++++++++++
.../src/dashboard/components/Header/index.jsx | 41 +++++----
.../src/dashboard/containers/DashboardHeader.jsx | 6 +-
.../src/reports/actions/reportState.js | 48 ++++++++++-
.../src/reports/reducers/reportState.js | 28 +++++-
6 files changed, 206 insertions(+), 24 deletions(-)
diff --git a/superset-frontend/src/common/components/index.tsx b/superset-frontend/src/common/components/index.tsx
index 58b9059..a509a73 100644
--- a/superset-frontend/src/common/components/index.tsx
+++ b/superset-frontend/src/common/components/index.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import React from 'react';
+import React, { RefObject } from 'react';
import { styled } from '@superset-ui/core';
import {
Dropdown,
@@ -217,6 +217,10 @@ export const MainNav = Object.assign(StyledNav, {
ItemGroup: AntdMenu.ItemGroup,
});
+interface ExtendedDropDownProps extends DropDownProps {
+ ref?: RefObject<HTMLDivElement>;
+}
+
export const Input = styled(AntdInput)`
border: 1px solid ${({ theme }) => theme.colors.secondary.light3};
border-radius: ${({ theme }) => theme.borderRadius}px;
@@ -233,7 +237,7 @@ export const TextArea = styled(AntdInput.TextArea)`
`;
export const NoAnimationDropdown = (
- props: DropDownProps & { children?: React.ReactNode },
+ props: ExtendedDropDownProps & { children?: React.ReactNode },
) => (
<Dropdown
overlayStyle={{ zIndex: 4000, animationDuration: '0s' }}
diff --git a/superset-frontend/src/components/ReportModal/HeaderReportActionsDropdown/index.tsx b/superset-frontend/src/components/ReportModal/HeaderReportActionsDropdown/index.tsx
new file mode 100644
index 0000000..5018729
--- /dev/null
+++ b/superset-frontend/src/components/ReportModal/HeaderReportActionsDropdown/index.tsx
@@ -0,0 +1,99 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { t } from '@superset-ui/core';
+import Icons from 'src/components/Icons';
+import { Switch } from 'src/components/Switch';
+import { AlertObject } from 'src/views/CRUD/alert/types';
+import { Menu, NoAnimationDropdown } from 'src/common/components';
+// function useOnClickOutside(ref: any, handler: any) {
+// useEffect(() => {
+// const listener = (event: any) => {
+// if (ref.current || ref.current.contains(event.target)) {
+// handler(event);
+// }
+// };
+// document.addEventListener('mousedown', listener, { capture: true });
+// document.addEventListener('touchstart', listener, { capture: true });
+// return () => {
+// document.removeEventListener('mousedown', listener, { capture: true });
+// document.removeEventListener('touchstart', listener, { capture: true });
+// };
+// }, [ref, handler]);
+// }
+
+export default function HeaderReportActionsDropDown({
+ // showReportModal,
+ // hideReportModal,
+ // addDangerToast,
+ toggleActive,
+}: {
+ showReportModal: () => void;
+ hideReportModal: () => void;
+ // addDangerToast: () => void;
+ toggleActive: (data: AlertObject, checked: boolean) => void;
+}) {
+ // const ref: any = useRef();
+ const report = useSelector<any, AlertObject>(
+ state => state.reportState.report.result[0],
+ );
+
+ const toggleActiveKey = async (data: AlertObject, checked: boolean) => {
+ if (data?.id) {
+ toggleActive(data, checked);
+ }
+ };
+
+ const menu = () => (
+ <Menu selectable={false}>
+ <Menu.Item>
+ {t('Email reports active')}
+ <Switch
+ data-test="toggle-active"
+ checked={report.active}
+ onClick={(checked: boolean) => toggleActiveKey(report, checked)}
+ size="small"
+ />
+ </Menu.Item>
+ <Menu.Item>
+ <div role="button" tabIndex={0}>
+ {' '}
+ {t('Edit email report')}
+ </div>
+ </Menu.Item>
+ <Menu.Item>{t('Delete email report')}</Menu.Item>
+ </Menu>
+ );
+
+ return (
+ <NoAnimationDropdown
+ // ref={ref}
+ overlay={menu()}
+ trigger={['click']}
+ getPopupContainer={(triggerNode: any) =>
+ triggerNode.closest('.action-button')
+ }
+ >
+ <span role="button" className="action-button" tabIndex={0}>
+ <Icons.Calendar />
+ </span>
+ </NoAnimationDropdown>
+ );
+}
diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx
index 5d0d546..70ac4db 100644
--- a/superset-frontend/src/dashboard/components/Header/index.jsx
+++ b/superset-frontend/src/dashboard/components/Header/index.jsx
@@ -35,6 +35,7 @@ import EditableTitle from 'src/components/EditableTitle';
import FaveStar from 'src/components/FaveStar';
import { safeStringify } from 'src/utils/safeStringify';
import HeaderActionsDropdown from 'src/dashboard/components/Header/HeaderActionsDropdown';
+import HeaderReportActionsDropdown from 'src/components/ReportModal/HeaderReportActionsDropdown';
import PublishedStatus from 'src/dashboard/components/PublishedStatus';
import UndoRedoKeyListeners from 'src/dashboard/components/UndoRedoKeyListeners';
import PropertiesModal from 'src/dashboard/components/PropertiesModal';
@@ -159,7 +160,7 @@ class Header extends React.PureComponent {
this.hidePropertiesModal = this.hidePropertiesModal.bind(this);
this.showReportModal = this.showReportModal.bind(this);
this.hideReportModal = this.hideReportModal.bind(this);
- this.handleReportClick = this.handleReportClick.bind(this);
+ this.renderReportModal = this.renderReportModal.bind(this);
}
componentDidMount() {
@@ -379,11 +380,27 @@ class Header extends React.PureComponent {
this.setState({ showingReportModal: false });
}
- handleReportClick() {
+ renderReportModal() {
const attachedReportExists = this.props.report?.count > 0;
- if (!attachedReportExists) {
- this.showReportModal();
- }
+ return attachedReportExists ? (
+ <HeaderReportActionsDropdown
+ showReportModal={this.showReportModal}
+ hideReportModal={this.hideReportModal}
+ toggleActive={this.props.toggleActive}
+ />
+ ) : (
+ <>
+ <span
+ role="button"
+ title={t('Schedule email report')}
+ tabIndex={0}
+ className="action-button"
+ onClick={this.showReportModal}
+ >
+ <Icons.Calendar />
+ </span>
+ </>
+ );
}
canAddReports() {
@@ -548,19 +565,7 @@ class Header extends React.PureComponent {
</span>
</>
)}
- {shouldShowReport && (
- <>
- <span
- role="button"
- title={t('Schedule email report')}
- tabIndex={0}
- className="action-button"
- onClick={this.handleReportClick}
- >
- <Icons.Calendar />
- </span>
- </>
- )}
+ {shouldShowReport && this.renderReportModal()}
{this.state.showingPropertiesModal && (
<PropertiesModal
diff --git a/superset-frontend/src/dashboard/containers/DashboardHeader.jsx b/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
index c57fceb..82e5ee7 100644
--- a/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
+++ b/superset-frontend/src/dashboard/containers/DashboardHeader.jsx
@@ -56,7 +56,10 @@ import {
import { logEvent } from '../../logger/actions';
import { DASHBOARD_HEADER_ID } from '../util/constants';
-import { fetchUISpecificReport } from '../../reports/actions/reportState';
+import {
+ fetchUISpecificReport,
+ toggleActive,
+} from '../../reports/actions/reportState';
function mapStateToProps({
dashboardLayout: undoableLayout,
@@ -127,6 +130,7 @@ function mapDispatchToProps(dispatch) {
dashboardTitleChanged,
updateDataMask,
fetchUISpecificReport,
+ toggleActive,
},
dispatch,
);
diff --git a/superset-frontend/src/reports/actions/reportState.js b/superset-frontend/src/reports/actions/reportState.js
index 847e677..8bed099 100644
--- a/superset-frontend/src/reports/actions/reportState.js
+++ b/superset-frontend/src/reports/actions/reportState.js
@@ -25,16 +25,17 @@ export const SET_REPORT = 'SET_REPORT';
export function setReport(report) {
return { type: SET_REPORT, report };
}
+
export function fetchUISpecificReport(
userId,
- search_field,
+ filter_field,
creation_method,
dashboardId,
) {
const queryParams = rison.encode({
filters: [
{
- col: search_field,
+ col: filter_field,
opr: 'eq',
value: dashboardId,
},
@@ -68,3 +69,46 @@ export function fetchUISpecificReport(
);
};
}
+
+export const EDIT_REPORT = 'EDIT_REPORT';
+
+export function reportEditor(report) {
+ return function (dispatch) {
+ SupersetClient.put({
+ endpoint: `/api/v1/report/${report.id}`,
+ postPayload: { report },
+ })
+ .then(() => dispatch({ type: EDIT_REPORT, report }))
+ .catch(() =>
+ dispatch(
+ addDangerToast(t('An error occurred while editing this report.')),
+ ),
+ );
+ };
+}
+
+export const TOGGLE_ACTIVE = 'TOGGLE_ACTIVE';
+export function toggleActiveKey(report, isActive) {
+ return { type: TOGGLE_ACTIVE, report, isActive };
+}
+export function toggleActive(report, isActive) {
+ return function toggleActiveThunk(dispatch) {
+ return SupersetClient.put({
+ endpoint: encodeURI(`/api/v1/report/${report.id}`),
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ active: isActive,
+ }),
+ })
+ .then(() => {
+ dispatch(toggleActiveKey(report, isActive));
+ })
+ .catch(() => {
+ dispatch(
+ addDangerToast(
+ t('We were unable to active or deactivate this report.'),
+ ),
+ );
+ });
+ };
+}
diff --git a/superset-frontend/src/reports/reducers/reportState.js b/superset-frontend/src/reports/reducers/reportState.js
index 16979ac..5946dbd 100644
--- a/superset-frontend/src/reports/reducers/reportState.js
+++ b/superset-frontend/src/reports/reducers/reportState.js
@@ -17,7 +17,8 @@
* under the License.
*/
/* eslint-disable camelcase */
-import { SET_REPORT } from '../actions/reportState';
+import { alterInArr } from 'src/reduxUtils';
+import { SET_REPORT, EDIT_REPORT, TOGGLE_ACTIVE } from '../actions/reportState';
export default function reportStateReducer(state = {}, action) {
const actionHandlers = {
@@ -27,6 +28,31 @@ export default function reportStateReducer(state = {}, action) {
report: action.report,
};
},
+ [EDIT_REPORT]() {
+ return {
+ ...state,
+ report: action.report,
+ };
+ },
+ [TOGGLE_ACTIVE]() {
+ const { report, isActive } = action;
+ const existing = state.report.result.find(
+ result => result.id === report.id,
+ );
+ const newArr = alterInArr(
+ state.report,
+ 'result',
+ existing,
+ {
+ active: isActive,
+ },
+ 'id',
+ );
+ return {
+ ...state,
+ report: newArr,
+ };
+ },
};
if (action.type in actionHandlers) {