Skip to content

Commit

Permalink
Preset io ch28954 refactor reports (apache#19129)
Browse files Browse the repository at this point in the history
* pexdax refactor (apache#16333)

* refactor progress (apache#16339)

* fix: Header Actions test refactor (apache#16336)

* fixed tests

* Update index.tsx

Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>

* code dry (apache#16358)

* Fetch bug fixed (apache#16376)

* continued refactoring (apache#16377)

* pexdax refactor (apache#16333)

* refactor progress (apache#16339)

* fix: Header Actions test refactor (apache#16336)

* fixed tests

* Update index.tsx

Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>

* code dry (apache#16358)

* Fetch bug fixed (apache#16376)

* continued refactoring (apache#16377)

* refactor: Arash/new state report (apache#16987)

* code dry (apache#16358)

* pexdax refactor (apache#16333)

* refactor progress (apache#16339)

* fix: Header Actions test refactor (apache#16336)

* fixed tests

* Update index.tsx

Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>

* Fetch bug fixed (apache#16376)

* continued refactoring (apache#16377)

* refactor(reports): Arash/refactor reports (apache#16855)

* pexdax refactor (apache#16333)

* refactor progress (apache#16339)

* fix: Header Actions test refactor (apache#16336)

* fixed tests

* Update index.tsx

Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>

* code dry (apache#16358)

* Fetch bug fixed (apache#16376)

* continued refactoring (apache#16377)

* refactor: Reports - ReportModal (apache#16622)

* refactoring progress

* removed consoles

* Working, but with 2 fetches

* report pickup

Co-authored-by: Lyndsi Kay Williams <55605634+lyndsiWilliams@users.noreply.github.com>
Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>

* refactor(reports):  Arash/again refactor reports (apache#16872)

* pexdax refactor (apache#16333)

* refactor progress (apache#16339)

* fix: Header Actions test refactor (apache#16336)

* fixed tests

* Update index.tsx

Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>

* code dry (apache#16358)

* Fetch bug fixed (apache#16376)

* continued refactoring (apache#16377)

* refactor: Reports - ReportModal (apache#16622)

* refactoring progress

* removed consoles

* Working, but with 2 fetches

* it is still not working

Co-authored-by: Lyndsi Kay Williams <55605634+lyndsiWilliams@users.noreply.github.com>
Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>

* next changes

Co-authored-by: Lyndsi Kay Williams <55605634+lyndsiWilliams@users.noreply.github.com>
Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>

* refactor: Reports code clean 10-29 (apache#17424)

* Add delete functionality

* Report schema restructure progress

* Fix lint

* Removed console.log

* fix(Explore): Remove changes to the properties on cancel (apache#17184)

* Remove on close

* Fix lint

* Add tests

* fix(dashboard): don't show report modal for anonymous user (apache#17106)

* Added sunburst echart

* fix(dashboard):Hide reports modal for anonymous users

* Address comments

* Make prettier happy

Co-authored-by: Mayur <mayurp@kpmg.com>

* fix(explore): Metric control breaks when saved metric deleted from dataset (apache#17503)

* Add functionality is now working (apache#17578)

* refactoring reports

* ready for review

* added testing

* removed user reducer

* elizabeth suggestions

Co-authored-by: Lyndsi Kay Williams <55605634+lyndsiWilliams@users.noreply.github.com>
Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>
Co-authored-by: Geido <60598000+geido@users.noreply.github.com>
Co-authored-by: Mayur <mayurnewase111@gmail.com>
Co-authored-by: Mayur <mayurp@kpmg.com>
Co-authored-by: Kamil Gabryjelski <kamil.gabryjelski@gmail.com>
  • Loading branch information
7 people committed May 9, 2022
1 parent 6b457e9 commit 3063cae
Show file tree
Hide file tree
Showing 17 changed files with 556 additions and 360 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* 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 * as React from 'react';
import userEvent from '@testing-library/user-event';
import { render, screen, act } from 'spec/helpers/testing-library';
import * as featureFlags from 'src/featureFlags';
import { FeatureFlag } from '@superset-ui/core';
import HeaderReportDropdown, { HeaderReportProps } from '.';

let isFeatureEnabledMock: jest.MockInstance<boolean, [string]>;

const createProps = () => ({
toggleActive: jest.fn(),
deleteActiveReport: jest.fn(),
dashboardId: 1,
});

const stateWithOnlyUser = {
explore: {
user: {
email: 'admin@test.com',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
createdOn: '2022-01-12T10:17:37.801361',
roles: { Admin: [['menu_access', 'Manage']] },
userId: 1,
username: 'admin',
},
},
reports: {},
};

const stateWithUserAndReport = {
explore: {
user: {
email: 'admin@test.com',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
createdOn: '2022-01-12T10:17:37.801361',
roles: { Admin: [['menu_access', 'Manage']] },
userId: 1,
username: 'admin',
},
},
reports: {
dashboards: {
1: {
id: 1,
result: {
active: true,
creation_method: 'dashboards',
crontab: '0 12 * * 1',
dashboard: 1,
name: 'Weekly Report',
owners: [1],
recipients: [
{
recipient_config_json: {
target: 'admin@test.com',
},
type: 'Email',
},
],
type: 'Report',
},
},
},
},
};

function setup(props: HeaderReportProps, initialState = {}) {
render(
<div>
<HeaderReportDropdown {...props} />
</div>,
{ useRedux: true, initialState },
);
}

describe('Header Report Dropdown', () => {
beforeAll(() => {
isFeatureEnabledMock = jest
.spyOn(featureFlags, 'isFeatureEnabled')
.mockImplementation(
(featureFlag: FeatureFlag) => featureFlag === FeatureFlag.ALERT_REPORTS,
);
});

afterAll(() => {
// @ts-ignore
isFeatureEnabledMock.restore();
});

it('renders correctly', () => {
const mockedProps = createProps();
act(() => {
setup(mockedProps, stateWithUserAndReport);
});
expect(screen.getByRole('button')).toBeInTheDocument();
});

it('renders the dropdown correctly', () => {
const mockedProps = createProps();
act(() => {
setup(mockedProps, stateWithUserAndReport);
});
const emailReportModalButton = screen.getByRole('button');
userEvent.click(emailReportModalButton);
expect(screen.getByText('Email reports active')).toBeInTheDocument();
expect(screen.getByText('Edit email report')).toBeInTheDocument();
expect(screen.getByText('Delete email report')).toBeInTheDocument();
});

it('opens an edit modal', () => {
const mockedProps = createProps();
act(() => {
setup(mockedProps, stateWithUserAndReport);
});
const emailReportModalButton = screen.getByRole('button');
userEvent.click(emailReportModalButton);
const editModal = screen.getByText('Edit email report');
userEvent.click(editModal);
expect(screen.getByText('Edit Email Report')).toBeInTheDocument();
});

it('opens a delete modal', () => {
const mockedProps = createProps();
act(() => {
setup(mockedProps, stateWithUserAndReport);
});
const emailReportModalButton = screen.getByRole('button');
userEvent.click(emailReportModalButton);
const deleteModal = screen.getByText('Delete email report');
userEvent.click(deleteModal);
expect(screen.getByText('Delete Report?')).toBeInTheDocument();
});

it('renders a new report modal if there is no report', () => {
const mockedProps = createProps();
act(() => {
setup(mockedProps, stateWithOnlyUser);
});
const emailReportModalButton = screen.getByRole('button');
userEvent.click(emailReportModalButton);
expect(screen.getByText('New Email Report')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/**
* 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, { useState, useEffect } from 'react';
import { usePrevious } from 'src/hooks/usePrevious';
import { useSelector, useDispatch } from 'react-redux';
import { t, SupersetTheme, css, useTheme } 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 } from 'src/components/Menu';
import { NoAnimationDropdown } from 'src/components/Dropdown';
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
import DeleteModal from 'src/components/DeleteModal';
import ReportModal from 'src/components/ReportModal';
import { ChartState } from 'src/explore/types';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
import { fetchUISpecificReport } from 'src/reports/actions/reports';
import { reportSelector } from 'src/views/CRUD/hooks';
import { ReportType } from 'src/dashboard/util/constants';

const deleteColor = (theme: SupersetTheme) => css`
color: ${theme.colors.error.base};
`;

export interface HeaderReportProps {
toggleActive: (data: AlertObject, isActive: boolean) => void;
deleteActiveReport: (data: AlertObject) => void;
dashboardId?: number;
chart?: ChartState;
}

export default function HeaderReportDropDown({
toggleActive,
deleteActiveReport,
dashboardId,
chart,
}: HeaderReportProps) {
const dispatch = useDispatch();

const report = useSelector<any, AlertObject>(state => {
const resourceType = dashboardId
? ReportType.DASHBOARDS
: ReportType.CHARTS;
return reportSelector(state, resourceType, dashboardId || chart?.id);
});
const user: UserWithPermissionsAndRoles = useSelector<
any,
UserWithPermissionsAndRoles
>(state => state.user || state.explore?.user);
const [currentReportDeleting, setCurrentReportDeleting] =
useState<AlertObject | null>(null);
const theme = useTheme();
const prevDashboard = usePrevious(dashboardId);
const [showModal, setShowModal] = useState<boolean>(false);
const toggleActiveKey = async (data: AlertObject, checked: boolean) => {
if (data?.id) {
toggleActive(data, checked);
}
};

const handleReportDelete = (report: AlertObject) => {
deleteActiveReport(report);
setCurrentReportDeleting(null);
};

const canAddReports = () => {
if (!isFeatureEnabled(FeatureFlag.ALERT_REPORTS)) {
return false;
}

if (!user?.userId) {
// this is in the case that there is an anonymous user.
return false;
}
const roles = Object.keys(user.roles || []);
const permissions = roles.map(key =>
user.roles[key].filter(
perms => perms[0] === 'menu_access' && perms[1] === 'Manage',
),
);
return permissions[0].length > 0;
};
const shouldFetch =
canAddReports() &&
!!((dashboardId && prevDashboard !== dashboardId) || chart?.id);

useEffect(() => {
if (shouldFetch) {
dispatch(
fetchUISpecificReport({
userId: user.userId,
filterField: dashboardId ? 'dashboard_id' : 'chart_id',
creationMethod: dashboardId ? 'dashboards' : 'charts',
resourceId: dashboardId || chart?.id,
}),
);
}
}, []);

const menu = () => (
<Menu selectable={false} css={{ width: '200px' }}>
<Menu.Item>
{t('Email reports active')}
<Switch
data-test="toggle-active"
checked={report?.active}
onClick={(checked: boolean) => toggleActiveKey(report, checked)}
size="small"
css={{ marginLeft: theme.gridUnit * 2 }}
/>
</Menu.Item>
<Menu.Item onClick={() => setShowModal(true)}>
{t('Edit email report')}
</Menu.Item>
<Menu.Item
onClick={() => setCurrentReportDeleting(report)}
css={deleteColor}
>
{t('Delete email report')}
</Menu.Item>
</Menu>
);
return (
<>
{canAddReports() && (
<>
<ReportModal
userId={user.userId}
showModal={showModal}
onHide={() => setShowModal(false)}
userEmail={user.email}
dashboardId={dashboardId}
chart={chart}
/>
{report ? (
<>
<NoAnimationDropdown
overlay={menu()}
trigger={['click']}
getPopupContainer={(triggerNode: any) =>
triggerNode.closest('.action-button')
}
>
<span role="button" className="action-button" tabIndex={0}>
<Icons.Calendar />
</span>
</NoAnimationDropdown>
{currentReportDeleting && (
<DeleteModal
description={t(
'This action will permanently delete %s.',
currentReportDeleting.name,
)}
onConfirm={() => {
if (currentReportDeleting) {
handleReportDelete(currentReportDeleting);
}
}}
onHide={() => setCurrentReportDeleting(null)}
open
title={t('Delete Report?')}
/>
)}
</>
) : (
<span
role="button"
title={t('Schedule email report')}
tabIndex={0}
className="action-button"
onClick={() => setShowModal(true)}
>
<Icons.Calendar />
</span>
)}
</>
)}
</>
);
}
Loading

0 comments on commit 3063cae

Please sign in to comment.