-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Preset io ch28954 refactor reports (#19129)
* pexdax refactor (#16333) * refactor progress (#16339) * fix: Header Actions test refactor (#16336) * fixed tests * Update index.tsx Co-authored-by: Elizabeth Thompson <eschutho@gmail.com> * code dry (#16358) * Fetch bug fixed (#16376) * continued refactoring (#16377) * pexdax refactor (#16333) * refactor progress (#16339) * fix: Header Actions test refactor (#16336) * fixed tests * Update index.tsx Co-authored-by: Elizabeth Thompson <eschutho@gmail.com> * code dry (#16358) * Fetch bug fixed (#16376) * continued refactoring (#16377) * refactor: Arash/new state report (#16987) * code dry (#16358) * pexdax refactor (#16333) * refactor progress (#16339) * fix: Header Actions test refactor (#16336) * fixed tests * Update index.tsx Co-authored-by: Elizabeth Thompson <eschutho@gmail.com> * Fetch bug fixed (#16376) * continued refactoring (#16377) * refactor(reports): Arash/refactor reports (#16855) * pexdax refactor (#16333) * refactor progress (#16339) * fix: Header Actions test refactor (#16336) * fixed tests * Update index.tsx Co-authored-by: Elizabeth Thompson <eschutho@gmail.com> * code dry (#16358) * Fetch bug fixed (#16376) * continued refactoring (#16377) * refactor: Reports - ReportModal (#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 (#16872) * pexdax refactor (#16333) * refactor progress (#16339) * fix: Header Actions test refactor (#16336) * fixed tests * Update index.tsx Co-authored-by: Elizabeth Thompson <eschutho@gmail.com> * code dry (#16358) * Fetch bug fixed (#16376) * continued refactoring (#16377) * refactor: Reports - ReportModal (#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 (#17424) * Add delete functionality * Report schema restructure progress * Fix lint * Removed console.log * fix(Explore): Remove changes to the properties on cancel (#17184) * Remove on close * Fix lint * Add tests * fix(dashboard): don't show report modal for anonymous user (#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 (#17503) * Add functionality is now working (#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
1 parent
4e8fc02
commit f27e620
Showing
16 changed files
with
555 additions
and
387 deletions.
There are no files selected for viewing
167 changes: 167 additions & 0 deletions
167
superset-frontend/src/components/ReportModal/HeaderReportDropdown/index.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); |
195 changes: 195 additions & 0 deletions
195
superset-frontend/src/components/ReportModal/HeaderReportDropdown/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
/** | ||
* 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, NoAnimationDropdown } from 'src/common/components'; | ||
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> | ||
)} | ||
</> | ||
)} | ||
</> | ||
); | ||
} |
Oops, something went wrong.