Skip to content

Commit

Permalink
feat: Self subscribe reports (apache#16027)
Browse files Browse the repository at this point in the history
Co-authored-by: Lyndsi Kay Williams <55605634+lyndsiWilliams@users.noreply.github.com>
Co-authored-by: AAfghahi <48933336+AAfghahi@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 2, 2021
1 parent 31d79ff commit 5031a67
Show file tree
Hide file tree
Showing 21 changed files with 1,040 additions and 18 deletions.
2 changes: 2 additions & 0 deletions superset-frontend/spec/fixtures/mockState.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import dashboardInfo from './mockDashboardInfo';
import { emptyFilters } from './mockDashboardFilters';
import dashboardState from './mockDashboardState';
import { sliceEntitiesForChart } from './mockSliceEntities';
import { user } from '../javascripts/sqllab/fixtures';

export default {
datasources,
Expand All @@ -40,5 +41,6 @@ export default {
dashboardState,
dashboardLayout,
messageToasts,
user,
impressionId: 'mock_impression_id',
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import EditableTitle from 'src/components/EditableTitle';

const saveSliceStub = jest.fn();
const updateChartTitleStub = jest.fn();
const fetchUISpecificReportStub = jest.fn();
const mockProps = {
actions: {
saveSlice: saveSliceStub,
Expand All @@ -42,11 +43,23 @@ const mockProps = {
form_data: {
viz_type: 'table',
},
user: {
createdOn: '2021-04-27T18:12:38.952304',
email: 'admin',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
roles: { Admin: Array(173) },
userId: 1,
username: 'admin',
},
timeout: 1000,
chart: {
id: 0,
queryResponse: {},
},
fetchUISpecificReport: fetchUISpecificReportStub,
chartHeight: '30px',
};

Expand Down
8 changes: 6 additions & 2 deletions superset-frontend/src/common/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -234,7 +238,7 @@ export const TextArea = styled(AntdInput.TextArea)`

// @z-index-below-dashboard-header (100) - 1 = 99
export const NoAnimationDropdown = (
props: DropDownProps & { children?: React.ReactNode },
props: ExtendedDropDownProps & { children?: React.ReactNode },
) => (
<Dropdown overlayStyle={{ zIndex: 99, animationDuration: '0s' }} {...props} />
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const StyledFormGroup = styled('div')`
-webkit-appearance: none;
margin: 0;
}
margin-bottom: ${({ theme }) => theme.gridUnit * 5}px;
margin-bottom: ${({ theme }) => theme.gridUnit * 3}px;
.ant-form-item {
margin-bottom: 0;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* 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 userEvent from '@testing-library/user-event';
import { render, screen } from 'spec/helpers/testing-library';
import ReportModal from '.';

describe('Email Report Modal', () => {
it('inputs respond correctly', () => {
render(<ReportModal show />, { useRedux: true });

// ----- Report name textbox
// Initial value
const reportNameTextbox = screen.getByTestId('report-name-test');
expect(reportNameTextbox).toHaveDisplayValue('Weekly Report');
// Type in the textbox and assert that it worked
userEvent.type(reportNameTextbox, 'Report name text test');
expect(reportNameTextbox).toHaveDisplayValue('Report name text test');

// ----- Report description textbox
// Initial value
const reportDescriptionTextbox = screen.getByTestId(
'report-description-test',
);
expect(reportDescriptionTextbox).toHaveDisplayValue('');
// Type in the textbox and assert that it worked
userEvent.type(reportDescriptionTextbox, 'Report description text test');
expect(reportDescriptionTextbox).toHaveDisplayValue(
'Report description text test',
);

// ----- Crontab
const crontabInputs = screen.getAllByRole('combobox');
expect(crontabInputs).toHaveLength(5);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* 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 } from 'react';
import { useSelector } from 'react-redux';
import { t, SupersetTheme, css } 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 DeleteModal from 'src/components/DeleteModal';

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

export default function HeaderReportActionsDropDown({
showReportModal,
toggleActive,
deleteActiveReport,
}: {
showReportModal: () => void;
toggleActive: (data: AlertObject, checked: boolean) => void;
deleteActiveReport: (data: AlertObject) => void;
}) {
const reports = useSelector<any, AlertObject>(state => state.reports);
const reportsIds = Object.keys(reports);
const report = reports[reportsIds[0]];
const [
currentReportDeleting,
setCurrentReportDeleting,
] = useState<AlertObject | null>(null);

const toggleActiveKey = async (data: AlertObject, checked: boolean) => {
if (data?.id) {
toggleActive(data, checked);
}
};

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

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 onClick={showReportModal}>{t('Edit email report')}</Menu.Item>
<Menu.Item
onClick={() => setCurrentReportDeleting(report)}
css={deleteColor}
>
{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>
{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?')}
/>
)}
</>
);
}
Loading

0 comments on commit 5031a67

Please sign in to comment.