diff --git a/src/components/Admin/__snapshots__/Admin.test.jsx.snap b/src/components/Admin/__snapshots__/Admin.test.jsx.snap index 6517454b24..b595539c81 100644 --- a/src/components/Admin/__snapshots__/Admin.test.jsx.snap +++ b/src/components/Admin/__snapshots__/Admin.test.jsx.snap @@ -6134,35 +6134,40 @@ exports[` renders correctly with error state 1`] = ` className="col" > diff --git a/src/components/Admin/index.jsx b/src/components/Admin/index.jsx index 9bfe42a77d..edb825a26e 100644 --- a/src/components/Admin/index.jsx +++ b/src/components/Admin/index.jsx @@ -1,11 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import Helmet from 'react-helmet'; -import { Icon } from '@edx/paragon'; +import { Icon, Alert } from '@edx/paragon'; +import { Error } from '@edx/paragon/icons'; import { Link } from 'react-router-dom'; import Hero from '../Hero'; -import StatusAlert from '../StatusAlert'; import EnrollmentsTable from '../EnrollmentsTable'; import RegisteredLearnersTable from '../RegisteredLearnersTable'; import EnrolledLearnersTable from '../EnrolledLearnersTable'; @@ -248,24 +248,30 @@ class Admin extends React.Component { renderErrorMessage() { return ( - + + + Unable to load overview + + {`Try refreshing your screen (${this.props.error.message})`} + ); } renderCsvErrorMessage(message) { return ( - + variant="danger" + icon={Error} + > + + Unable to Generate CSV Report + + {`Please try again. (${message})`} + ); } diff --git a/src/components/BulkEnrollmentPage/CourseSearchResults.jsx b/src/components/BulkEnrollmentPage/CourseSearchResults.jsx index 7f7a78fe88..babf3b302d 100644 --- a/src/components/BulkEnrollmentPage/CourseSearchResults.jsx +++ b/src/components/BulkEnrollmentPage/CourseSearchResults.jsx @@ -4,10 +4,10 @@ import React, { } from 'react'; import PropTypes from 'prop-types'; import { connectStateResults } from 'react-instantsearch-dom'; -import { DataTable, Skeleton } from '@edx/paragon'; +import { DataTable, Skeleton, Alert } from '@edx/paragon'; import { SearchContext, SearchPagination } from '@edx/frontend-enterprise-catalog-search'; +import { WarningFilled, Error } from '@edx/paragon/icons'; -import StatusAlert from '../StatusAlert'; import { CourseNameCell, FormattedDateCell } from './table/CourseSearchResultsCells'; import { BulkEnrollContext } from './BulkEnrollmentContext'; @@ -96,20 +96,22 @@ export const BaseCourseSearchResults = (props) => { if (!isSearchStalled && error) { return ( - + + {`${ERROR_MESSAGE} ${error.message}`} + ); } if (!isSearchStalled && searchResults?.nbHits === 0) { return ( - + + {NO_DATA_MESSAGE} + ); } diff --git a/src/components/BulkEnrollmentPage/CourseSearchResults.test.jsx b/src/components/BulkEnrollmentPage/CourseSearchResults.test.jsx index 4fbb9b01aa..0beb4ad3d4 100644 --- a/src/components/BulkEnrollmentPage/CourseSearchResults.test.jsx +++ b/src/components/BulkEnrollmentPage/CourseSearchResults.test.jsx @@ -7,10 +7,9 @@ import configureMockStore from 'redux-mock-store'; import userEvent from '@testing-library/user-event'; import thunk from 'redux-thunk'; import { SearchContext, SearchPagination } from '@edx/frontend-enterprise-catalog-search'; -import { Skeleton } from '@edx/paragon'; +import { Skeleton, Alert } from '@edx/paragon'; import { IntlProvider } from '@edx/frontend-platform/i18n'; -import StatusAlert from '../StatusAlert'; import BulkEnrollContextProvider from './BulkEnrollmentContext'; import { BaseCourseSearchResults, NO_DATA_MESSAGE, TABLE_HEADERS, @@ -158,7 +157,7 @@ describe('', () => { const wrapper = mount(); - expect(wrapper.find(StatusAlert)).toHaveLength(1); + expect(wrapper.find(Alert)).toHaveLength(1); expect(wrapper.text()).toContain(NO_DATA_MESSAGE); }); }); diff --git a/src/components/CodeSearchResults/CodeSearchResults.test.jsx b/src/components/CodeSearchResults/CodeSearchResults.test.jsx index be04bd8b12..aae70500a8 100644 --- a/src/components/CodeSearchResults/CodeSearchResults.test.jsx +++ b/src/components/CodeSearchResults/CodeSearchResults.test.jsx @@ -5,6 +5,7 @@ import { MemoryRouter } from 'react-router-dom'; import { mount } from 'enzyme'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import CodeSearchResults from './index'; @@ -77,12 +78,14 @@ describe('', () => { .create(( - + + + )) @@ -105,11 +108,13 @@ describe('', () => { .create(( - + + + )) @@ -164,11 +169,13 @@ describe('', () => { .create(( - + + + )) @@ -204,11 +211,13 @@ describe('', () => { .create(( - + + + )) @@ -235,11 +244,13 @@ describe('', () => { .create(( - + + + )) @@ -262,11 +273,13 @@ describe('', () => { .create(( - + + + )) @@ -305,11 +318,13 @@ describe('', () => { const wrapper = mount(( - + + + )); @@ -360,11 +375,13 @@ describe('', () => { const wrapper = mount(( - + + + )); @@ -405,11 +422,13 @@ describe('', () => { const wrapper = mount(( - + + + )); @@ -441,11 +460,13 @@ describe('', () => { const wrapper = mount(( - + + + )); diff --git a/src/components/CodeSearchResults/CodeSearchResultsTable.jsx b/src/components/CodeSearchResults/CodeSearchResultsTable.jsx index 49db162711..d488230d9a 100644 --- a/src/components/CodeSearchResults/CodeSearchResultsTable.jsx +++ b/src/components/CodeSearchResults/CodeSearchResultsTable.jsx @@ -13,28 +13,28 @@ import EcommerceApiService from '../../data/services/EcommerceApiService'; const tableColumns = [ { - label: 'Coupon Batch', - key: 'couponName', + Header: 'Coupon Batch', + accessor: 'couponName', }, { - label: 'Code', - key: 'code', + Header: 'Code', + accessor: 'code', }, { - label: 'Redeemed', - key: 'isRedeemed', + Header: 'Redeemed', + accessor: 'isRedeemed', }, { - label: 'Redemption Date', - key: 'redemptionDate', + Header: 'Redemption Date', + accessor: 'redemptionDate', }, { - label: 'Course Title', - key: 'courseTitle', + Header: 'Course Title', + accessor: 'courseTitle', }, { - label: 'Actions', - key: 'actions', + Header: 'Actions', + accessor: 'actions', }, ]; @@ -72,7 +72,7 @@ const searchParameter = (searchQuery) => { }; const handleTableColumns = (searchQuery) => { - const assignedToColumnIndex = tableColumns.findIndex(column => column.key === 'assignedTo'); + const assignedToColumnIndex = tableColumns.findIndex(column => column.accessor === 'assignedTo'); // If search is made by email, no need to show "Assigned To" field if (isValidEmail(searchQuery) === undefined && assignedToColumnIndex > -1) { // Remove "Assigned To" column if it already exists @@ -80,8 +80,8 @@ const handleTableColumns = (searchQuery) => { } else if (isValidEmail(searchQuery) !== undefined && assignedToColumnIndex === -1) { // Add "Assigned To" column if it doesn't already exist tableColumns.splice(4, 0, { - label: 'Assigned To', - key: 'assignedTo', + Header: 'Assigned To', + accessor: 'assignedTo', }); } return tableColumns; diff --git a/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap b/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap index d411e7d841..7531cc809f 100644 --- a/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap +++ b/src/components/CodeSearchResults/__snapshots__/CodeSearchResults.test.jsx.snap @@ -55,30 +55,39 @@ exports[` basic rendering should render empty table data 1` @@ -135,42 +144,49 @@ exports[` basic rendering should render error 1`] = ` Close search results @@ -602,332 +618,412 @@ exports[` basic rendering should render table data 1`] = `
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Coupon Batch - - Code - - Redeemed - - Redemption Date - - Course Title - - Actions -
- Test Coupon Name - - Y7XS3OGG7WB7KQ5R - - - - has been redeemed - - - August 28, 2019 - - Test Course Title - - - -
- Test Coupon Name 2 - - CMX9N1LPGTUSL7DU - - - - - - - - - - - - -
- Test Coupon Name - - Y7XS3OGG7WB7KQ5R - - - - has been redeemed - - - August 30, 2019 - - Test Course Title - - - -
- Test Coupon Name - - FAG2LVLNHAKIXQ0Q - - - - - - - - - - +
+ Showing 4 of 0. +
+ +
+
+
+
+
+
+
- - -
-
- - -
-
- +
- - - - + + + + @@ -1001,223 +1097,306 @@ exports[` basic rendering should render table data when sea >
- - - - - - - - - - - - - - - - - - - - - - - -
- Coupon Batch - - Code - - Redeemed - - Redemption Date - - Assigned To - - Course Title - - Actions -
- Test Coupon Name - - FAG2LVLNHAKIXQ0Q - - - - - - - - test@test.com - - - - - - | - -
-
- - -
-
- +
+
+ +
+
+
+
+ + diff --git a/src/components/CodeSearchResults/index.jsx b/src/components/CodeSearchResults/index.jsx index 124b222bc5..d6849650f6 100644 --- a/src/components/CodeSearchResults/index.jsx +++ b/src/components/CodeSearchResults/index.jsx @@ -1,10 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { TransitionReplace } from '@edx/paragon'; +import { TransitionReplace, Alert } from '@edx/paragon'; +import { CheckCircle } from '@edx/paragon/icons'; import { updateUrl } from '../../utils'; -import StatusAlert from '../StatusAlert'; import CodeSearchResultsHeading from './CodeSearchResultsHeading'; import CodeSearchResultsTable from './CodeSearchResultsTable'; @@ -58,21 +58,14 @@ class CodeSearchResults extends React.Component { }; renderSuccessMessage = options => ( - - ); - - renderErrorMessage = options => ( - + > + {options.message} + ); render() { diff --git a/src/components/CompletedLearnersTable/__snapshots__/CompletedLearnersTable.test.jsx.snap b/src/components/CompletedLearnersTable/__snapshots__/CompletedLearnersTable.test.jsx.snap index 54d25a5ba8..32a34e8029 100644 --- a/src/components/CompletedLearnersTable/__snapshots__/CompletedLearnersTable.test.jsx.snap +++ b/src/components/CompletedLearnersTable/__snapshots__/CompletedLearnersTable.test.jsx.snap @@ -2,30 +2,39 @@ exports[`CompletedLearnersTable renders empty state correctly 1`] = ` diff --git a/src/components/CompletedLearnersTable/index.jsx b/src/components/CompletedLearnersTable/index.jsx index e24c171493..c15484510f 100644 --- a/src/components/CompletedLearnersTable/index.jsx +++ b/src/components/CompletedLearnersTable/index.jsx @@ -6,13 +6,13 @@ import EnterpriseDataApiService from '../../data/services/EnterpriseDataApiServi const CompletedLearnersTable = () => { const tableColumns = [ { - label: 'Email', - key: 'user_email', + Header: 'Email', + accessor: 'user_email', columnSortable: true, }, { - label: 'Total Course Completed Count', - key: 'completed_courses', + Header: 'Total Course Completed Count', + accessor: 'completed_courses', columnSortable: true, }, ]; diff --git a/src/components/CouponDetails/constants.js b/src/components/CouponDetails/constants.js index 180decda7f..5c4e566a18 100644 --- a/src/components/CouponDetails/constants.js +++ b/src/components/CouponDetails/constants.js @@ -81,36 +81,36 @@ export const DETAILS_TEXT = { export const COLUMNS = { redemptions: { - label: 'Redemptions', - key: 'redemptions', + Header: 'Redemptions', + accessor: 'redemptions', }, code: { - label: 'Code', - key: 'code', + Header: 'Code', + accessor: 'code', }, assignmentsRemaining: { - label: 'Assignments remaining', - key: 'assignments_remaining', + Header: 'Assignments remaining', + accessor: 'assignments_remaining', }, actions: { - label: 'Actions', - key: 'actions', + Header: 'Actions', + accessor: 'actions', }, lastReminderDate: { - label: 'Last reminder date', - key: 'last_reminder_date', + Header: 'Last reminder date', + accessor: 'last_reminder_date', }, assignmentDate: { - label: 'Assignment date', - key: 'assignment_date', + Header: 'Assignment date', + accessor: 'assignment_date', }, assignedTo: { - label: 'Assigned to', - key: 'assigned_to', + Header: 'Assigned to', + accessor: 'assigned_to', }, redeemedBy: { - label: 'Redeemed by', - key: 'assigned_to', + Header: 'Redeemed by', + accessor: 'assigned_to', }, }; diff --git a/src/components/CouponDetails/index.jsx b/src/components/CouponDetails/index.jsx index 6e3e38abe7..541c5e5b7d 100644 --- a/src/components/CouponDetails/index.jsx +++ b/src/components/CouponDetails/index.jsx @@ -2,15 +2,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { - Button, CheckBox, Icon, + Button, CheckBox, Icon, Alert, } from '@edx/paragon'; +import { CheckCircle, Error as ErrorIcon } from '@edx/paragon/icons'; import TableContainer from '../../containers/TableContainer'; import DownloadCsvButton from '../../containers/DownloadCsvButton'; import CodeAssignmentModal from '../../containers/CodeAssignmentModal'; import CodeReminderModal from '../../containers/CodeReminderModal'; import CodeRevokeModal from '../../containers/CodeRevokeModal'; -import StatusAlert from '../StatusAlert'; import EcommerceApiService from '../../data/services/EcommerceApiService'; import { updateUrl } from '../../utils'; @@ -84,7 +84,7 @@ class CouponDetails extends React.Component { getNewColumns(selectedToggle) { const selectColumn = { - label: ( + Header: ( ), - key: 'select', + accessor: 'select', }; switch (selectedToggle) { @@ -234,7 +234,7 @@ class CouponDetails extends React.Component { const selectColumn = tableColumns.shift(); - selectColumn.label = React.cloneElement(selectColumn.label, { + selectColumn.Header = React.cloneElement(selectColumn.Header, { checked: allCodesForPageSelected, className: hasPartialSelection ? ['mixed'] : [], }); @@ -244,7 +244,7 @@ class CouponDetails extends React.Component { // attribute appropriately. // // TODO: Paragon now has an IndeterminateCheckbox that can be used here. - const selectAllCheckBoxRef = selectColumn.label.ref && selectColumn.label.ref.current; + const selectAllCheckBoxRef = selectColumn.Header.ref && selectColumn.Header.ref.current; const selectAllCheckBoxDOM = ( selectAllCheckBoxRef && document.getElementById(selectAllCheckBoxRef.props.id) ); @@ -476,12 +476,15 @@ class CouponDetails extends React.Component { renderErrorMessage({ title, message }) { return ( - + + + {title} + +

{message}

+
); } @@ -492,15 +495,20 @@ class CouponDetails extends React.Component { } = this.props; return ( - 0 || couponOverviewError })} - iconClassName="fa fa-check" - title={title} - message={message} + icon={CheckCircle} onClose={this.resetCodeActionStatus} dismissible - /> + > + + {title} + +

+ {message} +

+ ); } @@ -511,12 +519,15 @@ class CouponDetails extends React.Component { } = this.props; return ( - 0 || couponOverviewError })} - title={title} - message={message} - /> + > + + {title} + +

{message}

+ ); } @@ -618,6 +629,7 @@ class CouponDetails extends React.Component { - -
  • - +
  • +
  • + +
  • + + + - - - - + + + + diff --git a/src/components/EnrolledLearnersForInactiveCoursesTable/index.jsx b/src/components/EnrolledLearnersForInactiveCoursesTable/index.jsx index 9f4ca1e817..42ba57c441 100644 --- a/src/components/EnrolledLearnersForInactiveCoursesTable/index.jsx +++ b/src/components/EnrolledLearnersForInactiveCoursesTable/index.jsx @@ -7,23 +7,23 @@ import EnterpriseDataApiService from '../../data/services/EnterpriseDataApiServi const EnrolledLearnersForInactiveCoursesTable = () => { const tableColumns = [ { - label: 'Email', - key: 'user_email', + Header: 'Email', + accessor: 'user_email', columnSortable: true, }, { - label: 'Total Course Enrollment Count', - key: 'enrollment_count', + Header: 'Total Course Enrollment Count', + accessor: 'enrollment_count', columnSortable: true, }, { - label: 'Total Completed Courses Count', - key: 'course_completion_count', + Header: 'Total Completed Courses Count', + accessor: 'course_completion_count', columnSortable: true, }, { - label: 'Last Activity Date', - key: 'last_activity_date', + Header: 'Last Activity Date', + accessor: 'last_activity_date', columnSortable: true, }, ]; diff --git a/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap b/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap index d0e5bbe03d..ca62a1b304 100644 --- a/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap +++ b/src/components/EnrolledLearnersTable/__snapshots__/EnrolledLearnersTable.test.jsx.snap @@ -2,30 +2,39 @@ exports[`EnrolledLearnersTable renders empty state correctly 1`] = ` diff --git a/src/components/EnrolledLearnersTable/index.jsx b/src/components/EnrolledLearnersTable/index.jsx index 65e0b1d4fc..1dc1ff3eb2 100644 --- a/src/components/EnrolledLearnersTable/index.jsx +++ b/src/components/EnrolledLearnersTable/index.jsx @@ -7,18 +7,18 @@ import EnterpriseDataApiService from '../../data/services/EnterpriseDataApiServi const EnrolledLearnersTable = () => { const tableColumns = [ { - label: 'Email', - key: 'user_email', + Header: 'Email', + accessor: 'user_email', columnSortable: true, }, { - label: 'Account Created', - key: 'lms_user_created', + Header: 'Account Created', + accessor: 'lms_user_created', columnSortable: true, }, { - label: 'Total Course Enrollment Count', - key: 'enrollment_count', + Header: 'Total Course Enrollment Count', + accessor: 'enrollment_count', columnSortable: true, }, ]; diff --git a/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap b/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap index 4422854152..290ee70e95 100644 --- a/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap +++ b/src/components/EnrollmentsTable/__snapshots__/EnrollmentsTable.test.jsx.snap @@ -2,30 +2,39 @@ exports[`EnrollmentsTable renders empty state correctly 1`] = ` diff --git a/src/components/EnrollmentsTable/index.jsx b/src/components/EnrollmentsTable/index.jsx index e53a63b5be..e13ed6eba9 100644 --- a/src/components/EnrollmentsTable/index.jsx +++ b/src/components/EnrollmentsTable/index.jsx @@ -7,48 +7,48 @@ import EnterpriseDataApiService from '../../data/services/EnterpriseDataApiServi const EnrollmentsTable = () => { const enrollmentTableColumns = [ { - label: 'Email', - key: 'user_email', + Header: 'Email', + accessor: 'user_email', columnSortable: true, }, { - label: 'Course Title', - key: 'course_title', + Header: 'Course Title', + accessor: 'course_title', columnSortable: true, }, { - label: 'Course Price', - key: 'course_list_price', + Header: 'Course Price', + accessor: 'course_list_price', columnSortable: true, }, { - label: 'Start Date', - key: 'course_start_date', + Header: 'Start Date', + accessor: 'course_start_date', columnSortable: true, }, { - label: 'End Date', - key: 'course_end_date', + Header: 'End Date', + accessor: 'course_end_date', columnSortable: true, }, { - label: 'Passed Date', - key: 'passed_date', + Header: 'Passed Date', + accessor: 'passed_date', columnSortable: true, }, { - label: 'Current Grade', - key: 'current_grade', + Header: 'Current Grade', + accessor: 'current_grade', columnSortable: true, }, { - label: 'Progress Status', - key: 'progress_status', + Header: 'Progress Status', + accessor: 'progress_status', columnSortable: true, }, { - label: 'Last Activity Date', - key: 'last_activity_date', + Header: 'Last Activity Date', + accessor: 'last_activity_date', columnSortable: true, }, ]; diff --git a/src/components/EnterpriseApp/EnterpriseApp.test.jsx b/src/components/EnterpriseApp/EnterpriseApp.test.jsx index b235e12640..b8670c46b5 100644 --- a/src/components/EnterpriseApp/EnterpriseApp.test.jsx +++ b/src/components/EnterpriseApp/EnterpriseApp.test.jsx @@ -4,6 +4,7 @@ import { } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; +import { mount } from 'enzyme'; import EnterpriseApp from './index'; import { features } from '../../config'; @@ -11,6 +12,13 @@ import { EnterpriseSubsidiesContext } from '../EnterpriseSubsidiesContext'; import { SCHOLAR_THEME } from '../settings/data/constants'; features.SETTINGS_PAGE = true; +features.REPORTING_CONFIGURATIONS = true; +features.CODE_MANAGEMENT = true; +features.ANALYTICS = true; +features.SAML_CONFIGURATION = true; +features.EXTERNAL_LMS_CONFIGURATION = true; +features.SETTINGS_PAGE_LMS_TAB = true; +features.FEATURE_SSO_SETTINGS_TAB = true; const EnterpriseSubsidiesContextProvider = ({ children }) => ( ', () => { render(); expect(screen.getByText("Oops, sorry we can't find that page!")).toBeInTheDocument(); }); + + it('should test sidebar click on props change', () => { + const wrapper = mount(); + const instance = wrapper.instance(); + jest.spyOn(instance, 'handleSidebarMenuItemClick'); + wrapper.setProps({ location: { pathname: '/admin/lmsintegrations' } }); + expect(instance.handleSidebarMenuItemClick).toBeCalled(); + }); + it('should display error', () => { + const error = Error('error'); + render(); + expect(screen.queryByText('Error')).toBeInTheDocument(); + }); + it('should display loading', () => { + render(); + expect(screen.queryByText('Loading...')).toBeInTheDocument(); + }); + it('should enable code management screen', () => { + render(); + expect(screen.getByText('/admin/coupons/request-codes')).toBeInTheDocument(); + }); + it('should enable code reporting screen', () => { + render(); + expect(screen.getByText('/admin/reporting')).toBeInTheDocument(); + }); + it('should enable code subscriptions screen', () => { + render(); + expect(screen.getByText('/admin/subscriptions')).toBeInTheDocument(); + }); + it('should enable code analytics screen', () => { + render(); + expect(screen.getByText('/admin/analytics')).toBeInTheDocument(); + }); + it('should enable code samlconfiguration screen', () => { + render(); + expect(screen.getByText('/admin/samlconfiguration')).toBeInTheDocument(); + }); + it('should enable code lmsintegrations screen', () => { + render(); + expect(screen.getByText('/admin/lmsintegrations')).toBeInTheDocument(); + }); }); diff --git a/src/components/EnterpriseApp/index.jsx b/src/components/EnterpriseApp/index.jsx index f76c785e6b..7f828191b2 100644 --- a/src/components/EnterpriseApp/index.jsx +++ b/src/components/EnterpriseApp/index.jsx @@ -37,9 +37,7 @@ class EnterpriseApp extends React.Component { } componentDidUpdate(prevProps) { - const { - location: { pathname }, - } = this.props; + const { location: { pathname } } = this.props; if (pathname !== prevProps.location.pathname) { this.handleSidebarMenuItemClick(); diff --git a/src/components/EnterpriseList/index.jsx b/src/components/EnterpriseList/index.jsx index 990ab6f794..1c8aa519f0 100644 --- a/src/components/EnterpriseList/index.jsx +++ b/src/components/EnterpriseList/index.jsx @@ -103,8 +103,8 @@ class EnterpriseList extends React.Component { const { error, loading, enterpriseList } = this.props; const columns = [ { - label: 'Enterprise', - key: 'link', + Header: 'Enterprise', + accessor: 'link', }, ]; diff --git a/src/components/LearnerActivityTable/LearnerActivityTable.test.jsx b/src/components/LearnerActivityTable/LearnerActivityTable.test.jsx index 8d03a190c3..2db1783b4f 100644 --- a/src/components/LearnerActivityTable/LearnerActivityTable.test.jsx +++ b/src/components/LearnerActivityTable/LearnerActivityTable.test.jsx @@ -5,6 +5,7 @@ import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; import { mount } from 'enzyme'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import LearnerActivityTable from '.'; @@ -84,9 +85,11 @@ const learnerActivityStore = mockStore({ const LearnerActivityEmptyTableWrapper = props => ( - + + + ); @@ -94,9 +97,11 @@ const LearnerActivityEmptyTableWrapper = props => ( const LearnerActivityTableWrapper = props => ( - + + + ); @@ -212,6 +217,7 @@ describe('LearnerActivityTable', () => { 'Course Price', 'Start Date', 'End Date', + 'Passed Date', 'Current Grade', 'Progress Status', 'Last Activity Date', @@ -223,6 +229,7 @@ describe('LearnerActivityTable', () => { '$200', 'October 21, 2017', 'May 13, 2018', + 'September 23, 2018', '66%', 'Failed', 'September 22, 2018', @@ -233,6 +240,7 @@ describe('LearnerActivityTable', () => { '$200', 'October 21, 2017', 'May 13, 2018', + 'September 22, 2018', '80%', 'Passed', 'September 25, 2018', @@ -251,6 +259,7 @@ describe('LearnerActivityTable', () => { 'Course Price', 'Start Date', 'End Date', + 'Passed Date', 'Current Grade', 'Progress Status', 'Last Activity Date', @@ -262,6 +271,7 @@ describe('LearnerActivityTable', () => { '$200', 'October 21, 2017', 'May 13, 2018', + 'September 23, 2018', '66%', 'Failed', 'September 22, 2018', @@ -272,6 +282,7 @@ describe('LearnerActivityTable', () => { '$200', 'October 21, 2017', 'May 13, 2018', + 'September 22, 2018', '80%', 'Passed', 'September 25, 2018', diff --git a/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap b/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap index 4f4a143990..cfd71116ad 100644 --- a/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap +++ b/src/components/LearnerActivityTable/__snapshots__/LearnerActivityTable.test.jsx.snap @@ -13,476 +13,671 @@ exports[`LearnerActivityTable renders active learners table correctly 1`] = `
    - - - - - - - - - - click to sort - - - - - - - + + + + + + + + + + + + - - click to sort - - - - - - - + + + + + + + + + - - click to sort - - - - - - - + + + + + + + + + +
    - - - - - - - - + + + +
    -
    - + + + Course Title + + + + + + + + + + + Course Price + + + + + + + + + + + Start Date + + + + + + + + + + + End Date + + + + + + + + + + + Passed Date + + + + + + + + + + + Current Grade + + + + + + + + + + + Progress Status + + + + + + + + + + + Last Activity Date + + + + + + + +
    - + Dive into ReactJS + + $200 + + October 21, 2017 + + May 13, 2018 + + September 23, 2018 + + 66% + + Failed + + September 22, 2018 +
    + + new@example.com + + + + Redux with ReactJS + + $200 + + October 21, 2017 + + May 13, 2018 + + September 22, 2018 + + 80% + + Passed + + September 25, 2018 +
    +
    +
    - - - - - - - - - - - - awesome.me@example.com - - - - Dive into ReactJS - - - $200 - - - October 21, 2017 - - - May 13, 2018 - - - September 23, 2018 - - - 66% - - - Failed - - - September 22, 2018 - - - - - - new@example.com - - - - Redux with ReactJS - - - $200 - - - October 21, 2017 - - - May 13, 2018 - - - September 22, 2018 - - - 80% - - - Passed - - - September 25, 2018 - - - - -
    - - -
    -
    - +
    - - - - + + + + @@ -490,30 +685,39 @@ exports[`LearnerActivityTable renders active learners table correctly 1`] = ` exports[`LearnerActivityTable renders empty state correctly 1`] = ` @@ -532,438 +736,671 @@ exports[`LearnerActivityTable renders inactive past month learners table correct
    - - - - - - - - - click to sort - - - - - - - + + + + + + + + + + + + - - click to sort - - - - - - - + + + + + + + + + - - click to sort - - - - - - - + + + + + + + + + +
    - - - - - - + + + +
    -
    - + + + Course Title + + + + + + + + + + + Course Price + + + + + + + + + + + Start Date + + + + + + + + + + + End Date + + + + + + + + + + + Passed Date + + + + + + + + + + + Current Grade + + + + + + + + + + + Progress Status + + + + + + + + + + + Last Activity Date + + + + + + + +
    - + Dive into ReactJS + + $200 + + October 21, 2017 + + May 13, 2018 + + September 23, 2018 + + 66% + + Failed + + September 22, 2018 +
    + + new@example.com + + + + Redux with ReactJS + + $200 + + October 21, 2017 + + May 13, 2018 + + September 22, 2018 + + 80% + + Passed + + September 25, 2018 +
    +
    +
    - - - - - - - - - - - - awesome.me@example.com - - - - Dive into ReactJS - - - $200 - - - October 21, 2017 - - - May 13, 2018 - - - 66% - - - Failed - - - September 22, 2018 - - - - - - new@example.com - - - - Redux with ReactJS - - - $200 - - - October 21, 2017 - - - May 13, 2018 - - - 80% - - - Passed - - - September 25, 2018 - - - - -
    - - -
    -
    - +
    - - - - + + + + @@ -982,438 +1419,671 @@ exports[`LearnerActivityTable renders inactive past week learners table correctl
    - - - - - - - - - click to sort - - - - - - - + + + + + + + + + + + + - - click to sort - - - - - - - + + + + + + + + + - - click to sort - - - - - - - + + + + + + + + + +
    - - - - - - + + + +
    -
    - + + + Course Title + + + + + + + + + + + Course Price + + + + + + + + + + + Start Date + + + + + + + + + + + End Date + + + + + + + + + + + Passed Date + + + + + + + + + + + Current Grade + + + + + + + + + + + Progress Status + + + + + + + + + + + Last Activity Date + + + + + + + +
    - + Dive into ReactJS + + $200 + + October 21, 2017 + + May 13, 2018 + + September 23, 2018 + + 66% + + Failed + + September 22, 2018 +
    + + new@example.com + + + + Redux with ReactJS + + $200 + + October 21, 2017 + + May 13, 2018 + + September 22, 2018 + + 80% + + Passed + + September 25, 2018 +
    +
    +
    - - - - - - - - - - - - awesome.me@example.com - - - - Dive into ReactJS - - - $200 - - - October 21, 2017 - - - May 13, 2018 - - - 66% - - - Failed - - - September 22, 2018 - - - - - - new@example.com - - - - Redux with ReactJS - - - $200 - - - October 21, 2017 - - - May 13, 2018 - - - 80% - - - Passed - - - September 25, 2018 - - - - -
    - - -
    -
    - +
    - - -
  • - -
  • - - + + + + diff --git a/src/components/LearnerActivityTable/index.jsx b/src/components/LearnerActivityTable/index.jsx index 45fd3ac87a..a4de34e471 100644 --- a/src/components/LearnerActivityTable/index.jsx +++ b/src/components/LearnerActivityTable/index.jsx @@ -10,48 +10,48 @@ class LearnerActivityTable extends React.Component { const { activity } = this.props; const tableColumns = [ { - label: 'Email', - key: 'user_email', + Header: 'Email', + accessor: 'user_email', columnSortable: true, }, { - label: 'Course Title', - key: 'course_title', + Header: 'Course Title', + accessor: 'course_title', columnSortable: true, }, { - label: 'Course Price', - key: 'course_list_price', + Header: 'Course Price', + accessor: 'course_list_price', columnSortable: true, }, { - label: 'Start Date', - key: 'course_start_date', + Header: 'Start Date', + accessor: 'course_start_date', columnSortable: true, }, { - label: 'End Date', - key: 'course_end_date', + Header: 'End Date', + accessor: 'course_end_date', columnSortable: true, }, { - label: 'Passed Date', - key: 'passed_date', + Header: 'Passed Date', + accessor: 'passed_date', columnSortable: true, }, { - label: 'Current Grade', - key: 'current_grade', + Header: 'Current Grade', + accessor: 'current_grade', columnSortable: true, }, { - label: 'Progress Status', - key: 'progress_status', + Header: 'Progress Status', + accessor: 'progress_status', columnSortable: true, }, { - label: 'Last Activity Date', - key: 'last_activity_date', + Header: 'Last Activity Date', + accessor: 'last_activity_date', columnSortable: true, }, ]; diff --git a/src/components/LmsConfigurations/BlackboardIntegrationConfigForm.jsx b/src/components/LmsConfigurations/BlackboardIntegrationConfigForm.jsx index a756370a8f..d19abb7790 100644 --- a/src/components/LmsConfigurations/BlackboardIntegrationConfigForm.jsx +++ b/src/components/LmsConfigurations/BlackboardIntegrationConfigForm.jsx @@ -7,10 +7,12 @@ import { StatefulButton, Icon, Hyperlink, + Alert, } from '@edx/paragon'; +import { Error } from '@edx/paragon/icons'; + import { snakeCaseFormData } from '../../utils'; import LmsApiService from '../../data/services/LmsApiService'; -import StatusAlert from '../StatusAlert'; import SUBMIT_STATES from '../../data/constants/formSubmissions'; import { handleErrors, validateLmsConfigForm } from './common'; @@ -105,12 +107,15 @@ class BlackboardIntegrationConfigForm extends React.Component { if (error) { errorAlert = (
    - + + + Unable to submit config form: + +

    {error}

    +
    ); } diff --git a/src/components/LmsConfigurations/CanvasIntegrationConfigForm.jsx b/src/components/LmsConfigurations/CanvasIntegrationConfigForm.jsx index 23973bed6f..944e549d34 100644 --- a/src/components/LmsConfigurations/CanvasIntegrationConfigForm.jsx +++ b/src/components/LmsConfigurations/CanvasIntegrationConfigForm.jsx @@ -2,10 +2,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import isEmpty from 'lodash/isEmpty'; import { - ValidationFormGroup, Input, StatefulButton, Icon, + ValidationFormGroup, Input, StatefulButton, Icon, Alert, } from '@edx/paragon'; +import { Error } from '@edx/paragon/icons'; + import { snakeCaseFormData } from '../../utils'; -import StatusAlert from '../StatusAlert'; import LmsApiService from '../../data/services/LmsApiService'; import SUBMIT_STATES from '../../data/constants/formSubmissions'; import { handleErrors, validateLmsConfigForm } from './common'; @@ -96,12 +97,15 @@ class CanvasIntegrationConfigForm extends React.Component { if (error) { errorAlert = (
    - + + + Unable to submit config form: + +

    {error}

    +
    ); } diff --git a/src/components/LmsConfigurations/CornerstoneIntegrationConfigForm.jsx b/src/components/LmsConfigurations/CornerstoneIntegrationConfigForm.jsx index 3c5729d369..2c4ffc45fa 100644 --- a/src/components/LmsConfigurations/CornerstoneIntegrationConfigForm.jsx +++ b/src/components/LmsConfigurations/CornerstoneIntegrationConfigForm.jsx @@ -2,11 +2,12 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import isEmpty from 'lodash/isEmpty'; import { - ValidationFormGroup, Input, StatefulButton, Icon, + ValidationFormGroup, Input, StatefulButton, Icon, Alert, } from '@edx/paragon'; +import { Error } from '@edx/paragon/icons'; + import { snakeCaseFormData } from '../../utils'; import LmsApiService from '../../data/services/LmsApiService'; -import StatusAlert from '../StatusAlert'; import SUBMIT_STATES from '../../data/constants/formSubmissions'; import { handleErrors, validateLmsConfigForm } from './common'; @@ -152,13 +153,16 @@ function CornerstoneIntegrationConfigForm({ enterpriseId, config }) { if (state.error) { errorAlert = (
    - + > + + Unable to submit config form: + +

    {state.error}

    +
    ); } diff --git a/src/components/LmsConfigurations/CornerstoneIntegrationConfigForm.test.jsx b/src/components/LmsConfigurations/CornerstoneIntegrationConfigForm.test.jsx index bf99c3dee5..163edcf763 100644 --- a/src/components/LmsConfigurations/CornerstoneIntegrationConfigForm.test.jsx +++ b/src/components/LmsConfigurations/CornerstoneIntegrationConfigForm.test.jsx @@ -4,6 +4,12 @@ import { import '@testing-library/jest-dom/extend-expect'; import React from 'react'; import snakeCase from 'lodash/snakeCase'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { MemoryRouter } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; + import CornerstoneIntegrationConfigForm from './CornerstoneIntegrationConfigForm'; import LmsApiService from '../../data/services/LmsApiService'; @@ -13,10 +19,22 @@ jest.mock('../../data/services/LmsApiService', () => ({ })); const enterpriseId = 'test-enterprise'; +const mockStore = configureMockStore([thunk]); +const store = mockStore({}); + +const CornerstoneIntegrationConfigFormWrapper = props => ( + + + + + + + +); describe('', () => { test('renders Cornerstone Config Form', () => { - render(); + render(); // Verify all expected fields are present. screen.getByLabelText('Active'); screen.getByLabelText('Cornerstone Instance URL'); @@ -27,13 +45,13 @@ describe('', () => { active: true, cornerstoneBaseUrl: 'initial_url', }; - render(); + render(); expect(screen.getByLabelText('Active')).toBeChecked(); expect(screen.getByLabelText('Cornerstone Instance URL')).toHaveValue('initial_url'); }); test('required fields show as invalid when not filled in', () => { - render(); + render(); fireEvent.click(screen.getByText('Submit')); expect(screen.getByLabelText('Cornerstone Instance URL')).toHaveClass('is-invalid'); @@ -49,7 +67,7 @@ describe('', () => { enterprise_customer: enterpriseId, }; - render(); + render(); fireEvent.change(screen.getByLabelText('Cornerstone Instance URL'), { target: { value: 'testinstance' }, }); @@ -66,7 +84,7 @@ describe('', () => { cornerstoneBaseUrl: 'testinstance', }; - render(); + render(); fireEvent.change(screen.getByLabelText('Cornerstone Instance URL'), { target: { value: 'changedURL' }, }); diff --git a/src/components/LmsConfigurations/DegreedIntegrationConfigForm.jsx b/src/components/LmsConfigurations/DegreedIntegrationConfigForm.jsx index 9ba5208a45..ce0470872d 100644 --- a/src/components/LmsConfigurations/DegreedIntegrationConfigForm.jsx +++ b/src/components/LmsConfigurations/DegreedIntegrationConfigForm.jsx @@ -2,11 +2,12 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import isEmpty from 'lodash/isEmpty'; import { - ValidationFormGroup, Input, StatefulButton, Icon, + ValidationFormGroup, Input, StatefulButton, Icon, Alert, } from '@edx/paragon'; +import { Error } from '@edx/paragon/icons'; + import { snakeCaseFormData } from '../../utils'; import LmsApiService from '../../data/services/LmsApiService'; -import StatusAlert from '../StatusAlert'; import SUBMIT_STATES from '../../data/constants/formSubmissions'; import { handleErrors, validateLmsConfigForm } from './common'; @@ -157,13 +158,14 @@ function DegreedIntegrationConfigForm({ enterpriseId, config }) { if (state.error) { errorAlert = (
    - + > + Unable to submit config form: +

    {state.error}

    +
    ); } diff --git a/src/components/LmsConfigurations/DegreedIntegrationConfigForm.test.jsx b/src/components/LmsConfigurations/DegreedIntegrationConfigForm.test.jsx index 19659d15df..beb4f26ff4 100644 --- a/src/components/LmsConfigurations/DegreedIntegrationConfigForm.test.jsx +++ b/src/components/LmsConfigurations/DegreedIntegrationConfigForm.test.jsx @@ -4,6 +4,12 @@ import { import '@testing-library/jest-dom/extend-expect'; import React from 'react'; import snakeCase from 'lodash/snakeCase'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { MemoryRouter } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; + import DegreedIntegrationConfigForm from './DegreedIntegrationConfigForm'; import LmsApiService from '../../data/services/LmsApiService'; @@ -13,10 +19,22 @@ jest.mock('../../data/services/LmsApiService', () => ({ })); const enterpriseId = 'test-enterprise'; +const mockStore = configureMockStore([thunk]); +const store = mockStore({}); + +const DegreedIntegrationConfigFormWrapper = props => ( + + + + + + + +); describe('', () => { test('renders Degreed Config Form', () => { - render(); + render(); // Verify all expected fields are present. screen.getByLabelText('Active'); screen.getByLabelText('Degreed User ID'); @@ -37,7 +55,7 @@ describe('', () => { key: 'initial_key', secret: 'initial_secret', }; - render(); + render(); expect(screen.getByLabelText('Active')).toBeChecked(); expect(screen.getByLabelText('Degreed User ID')).toHaveValue('initial_id'); expect(screen.getByLabelText('Degreed User Password')).toHaveValue('initial_pass'); @@ -48,7 +66,7 @@ describe('', () => { }); test('required fields show as invalid when not filled in', () => { - render(); + render(); fireEvent.click(screen.getByText('Submit')); expect(screen.getByLabelText('Degreed User ID')).toHaveClass('is-invalid'); @@ -74,7 +92,7 @@ describe('', () => { enterprise_customer: enterpriseId, }; - render(); + render(); fireEvent.change(screen.getByLabelText('Degreed User ID'), { target: { value: 'testuserid' }, }); @@ -111,7 +129,7 @@ describe('', () => { secret: 'testsecret', }; - render(); + render(); fireEvent.change(screen.getByLabelText('Degreed User ID'), { target: { value: 'changedUserId' }, }); diff --git a/src/components/LmsConfigurations/MoodleIntegrationConfigForm.jsx b/src/components/LmsConfigurations/MoodleIntegrationConfigForm.jsx index f7495ae560..cf345d1288 100644 --- a/src/components/LmsConfigurations/MoodleIntegrationConfigForm.jsx +++ b/src/components/LmsConfigurations/MoodleIntegrationConfigForm.jsx @@ -2,11 +2,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import isEmpty from 'lodash/isEmpty'; import { - ValidationFormGroup, Input, StatefulButton, Icon, + ValidationFormGroup, Input, StatefulButton, Icon, Alert, } from '@edx/paragon'; +import { Error } from '@edx/paragon/icons'; + import { snakeCaseFormData } from '../../utils'; import LmsApiService from '../../data/services/LmsApiService'; -import StatusAlert from '../StatusAlert'; import SUBMIT_STATES from '../../data/constants/formSubmissions'; import { handleErrors, validateLmsConfigForm } from './common'; @@ -116,12 +117,13 @@ class MoodleIntegrationConfigForm extends React.Component { if (error) { errorAlert = (
    - + + Unable to submit config form: +

    {error}

    +
    ); } diff --git a/src/components/LmsConfigurations/SuccessFactorsIntegrationConfigForm.jsx b/src/components/LmsConfigurations/SuccessFactorsIntegrationConfigForm.jsx index 807a76bfbc..c1b5a4cb10 100644 --- a/src/components/LmsConfigurations/SuccessFactorsIntegrationConfigForm.jsx +++ b/src/components/LmsConfigurations/SuccessFactorsIntegrationConfigForm.jsx @@ -2,11 +2,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import isEmpty from 'lodash/isEmpty'; import { - ValidationFormGroup, Input, StatefulButton, Icon, + ValidationFormGroup, Input, StatefulButton, Icon, Alert, } from '@edx/paragon'; +import { Error } from '@edx/paragon/icons'; + import { snakeCaseFormData } from '../../utils'; import LmsApiService from '../../data/services/LmsApiService'; -import StatusAlert from '../StatusAlert'; import SUBMIT_STATES from '../../data/constants/formSubmissions'; import { handleErrors, validateLmsConfigForm } from './common'; @@ -317,12 +318,13 @@ class SuccessFactorsIntegrationConfigForm extends React.Component {
    - + + Unable to submit config form: +

    {error}

    +
    diff --git a/src/components/LmsConfigurations/SuccessFactorsIntegrationConfigForm.test.jsx b/src/components/LmsConfigurations/SuccessFactorsIntegrationConfigForm.test.jsx index 040a2989b1..3cdf55e2c2 100644 --- a/src/components/LmsConfigurations/SuccessFactorsIntegrationConfigForm.test.jsx +++ b/src/components/LmsConfigurations/SuccessFactorsIntegrationConfigForm.test.jsx @@ -38,9 +38,9 @@ describe('', () => { expect(spyUpdate).toHaveBeenCalledWith(formData, config.id); }); - it('show StatusAlert on errors', () => { + it('show Alert on errors', () => { const wrapper = mount(()); wrapper.setState({ error: 'error occurred.' }); - expect(wrapper.find('StatusAlert').first().props().message).toEqual('error occurred.'); + expect(wrapper.find('Alert').find('AlertHeading').first().text()).toEqual('Unable to submit config form:'); }); }); diff --git a/src/components/PastWeekPassedLearnersTable/PastWeekPassedLearnersTable.test.jsx b/src/components/PastWeekPassedLearnersTable/PastWeekPassedLearnersTable.test.jsx index a4590b162f..9b88dba991 100644 --- a/src/components/PastWeekPassedLearnersTable/PastWeekPassedLearnersTable.test.jsx +++ b/src/components/PastWeekPassedLearnersTable/PastWeekPassedLearnersTable.test.jsx @@ -5,6 +5,7 @@ import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { Provider } from 'react-redux'; import { mount } from 'enzyme'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import PastWeekPassedLearnersTable from '.'; @@ -51,9 +52,11 @@ const store = mockStore({ const PastWeekPassedLearnersWrapper = props => ( - + + + ); diff --git a/src/components/PastWeekPassedLearnersTable/__snapshots__/PastWeekPassedLearnersTable.test.jsx.snap b/src/components/PastWeekPassedLearnersTable/__snapshots__/PastWeekPassedLearnersTable.test.jsx.snap index 5bd0b51726..44a1bfacac 100644 --- a/src/components/PastWeekPassedLearnersTable/__snapshots__/PastWeekPassedLearnersTable.test.jsx.snap +++ b/src/components/PastWeekPassedLearnersTable/__snapshots__/PastWeekPassedLearnersTable.test.jsx.snap @@ -13,248 +13,341 @@ exports[`PastWeekPassedLearnersTable renders table correctly 1`] = `
    - - - - - - - - click to sort - - - - - - - - - - - - - - - - - - - - -
    - - - - + + + +
    -
    - - awesome.me@example.com - - - Dive into ReactJS - - September 23, 2018 -
    - - new@example.com - - - Redux with ReactJS - - September 22, 2018 -
    -
    - - -
    -
    - +
    - - - - + + + + diff --git a/src/components/PastWeekPassedLearnersTable/index.jsx b/src/components/PastWeekPassedLearnersTable/index.jsx index 6a19f3fcb1..6dfc3d1182 100644 --- a/src/components/PastWeekPassedLearnersTable/index.jsx +++ b/src/components/PastWeekPassedLearnersTable/index.jsx @@ -7,18 +7,18 @@ import { formatTimestamp } from '../../utils'; const PastWeekPassedLearnersTable = () => { const tableColumns = [ { - label: 'Email', - key: 'user_email', + Header: 'Email', + accessor: 'user_email', columnSortable: true, }, { - label: 'Course Title', - key: 'course_title', + Header: 'Course Title', + accessor: 'course_title', columnSortable: true, }, { - label: 'Passed Date', - key: 'passed_date', + Header: 'Passed Date', + accessor: 'passed_date', columnSortable: true, }, ]; diff --git a/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap b/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap index d9248f4dbc..41ad7ef358 100644 --- a/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap +++ b/src/components/RegisteredLearnersTable/__snapshots__/RegisteredLearnersTable.test.jsx.snap @@ -2,30 +2,39 @@ exports[`RegisteredLearnersTable renders empty state correctly 1`] = ` diff --git a/src/components/RegisteredLearnersTable/index.jsx b/src/components/RegisteredLearnersTable/index.jsx index e2e60636f2..0a357107dd 100644 --- a/src/components/RegisteredLearnersTable/index.jsx +++ b/src/components/RegisteredLearnersTable/index.jsx @@ -8,13 +8,13 @@ import EnterpriseDataApiService from '../../data/services/EnterpriseDataApiServi const RegisteredLearnersTable = () => { const tableColumns = [ { - label: 'Email', - key: 'user_email', + Header: 'Email', + accessor: 'user_email', columnSortable: true, }, { - label: 'Account Created', - key: 'lms_user_created', + Header: 'Account Created', + accessor: 'lms_user_created', columnSortable: true, }, ]; diff --git a/src/components/RequestCodesPage/RequestCodesForm.jsx b/src/components/RequestCodesPage/RequestCodesForm.jsx index 1748caf8db..6dbfc7bb1d 100644 --- a/src/components/RequestCodesPage/RequestCodesForm.jsx +++ b/src/components/RequestCodesPage/RequestCodesForm.jsx @@ -2,10 +2,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Field, reduxForm } from 'redux-form'; import { Link, Redirect } from 'react-router-dom'; -import { Button, Icon } from '@edx/paragon'; +import { Button, Icon, Alert } from '@edx/paragon'; +import { Error } from '@edx/paragon/icons'; import RenderField from '../RenderField'; -import StatusAlert from '../StatusAlert'; import { isRequired, isValidEmail, isValidNumber, maxLength512, @@ -29,13 +29,14 @@ class RequestCodesForm extends React.Component { const { error: { message } } = this.props; return ( - + variant="danger" + icon={Error} + > + Unable to request more codes +

    {`Try refreshing your screen (${message})`}

    + ); } diff --git a/src/components/SamlProviderConfiguration/SamlProviderConfigForm.jsx b/src/components/SamlProviderConfiguration/SamlProviderConfigForm.jsx index fd7188d833..23003d55f0 100644 --- a/src/components/SamlProviderConfiguration/SamlProviderConfigForm.jsx +++ b/src/components/SamlProviderConfiguration/SamlProviderConfigForm.jsx @@ -2,9 +2,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import isEmpty from 'lodash/isEmpty'; import { - ValidationFormGroup, Input, StatefulButton, Icon, Button, + ValidationFormGroup, Input, StatefulButton, Icon, Button, Alert, } from '@edx/paragon'; -import StatusAlert from '../StatusAlert'; +import { Error } from '@edx/paragon/icons'; import SamlConfiguration from '../SamlConfiguration'; import SUBMIT_STATES from '../../data/constants/formSubmissions'; @@ -89,12 +89,13 @@ class SamlProviderConfigForm extends React.Component { if (error) { errorAlert = (
    - + + Unable to submit config form: +

    {error}

    +
    ); } diff --git a/src/components/SamlProviderConfiguration/SamlProviderDataForm.jsx b/src/components/SamlProviderConfiguration/SamlProviderDataForm.jsx index 95a2f09476..29600eba80 100644 --- a/src/components/SamlProviderConfiguration/SamlProviderDataForm.jsx +++ b/src/components/SamlProviderConfiguration/SamlProviderDataForm.jsx @@ -2,9 +2,10 @@ import React from 'react'; import PropTypes from 'prop-types'; import isEmpty from 'lodash/isEmpty'; import { - ValidationFormGroup, Input, StatefulButton, Icon, Button, + ValidationFormGroup, Input, StatefulButton, Icon, Button, Alert, } from '@edx/paragon'; -import StatusAlert from '../StatusAlert'; +import { Error } from '@edx/paragon/icons'; + import SUBMIT_STATES from '../../data/constants/formSubmissions'; export const REQUIRED_DATA_FIELDS = [ @@ -79,12 +80,13 @@ class SamlProviderDataForm extends React.Component { if (error) { errorAlert = (
    - + + Unable to submit Data form: +

    {error}

    +
    ); } diff --git a/src/components/StatusAlert/_StatusAlert.scss b/src/components/StatusAlert/_StatusAlert.scss deleted file mode 100644 index 53582680ea..0000000000 --- a/src/components/StatusAlert/_StatusAlert.scss +++ /dev/null @@ -1,12 +0,0 @@ -.alert { - .icon { - .fa { - font-size: $font-size-lg; - } - } - - .title { - font-weight: 600; - display: block; - } -} diff --git a/src/components/StatusAlert/index.jsx b/src/components/StatusAlert/index.jsx deleted file mode 100644 index 290f9b6c5d..0000000000 --- a/src/components/StatusAlert/index.jsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react'; -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import { StatusAlert as Alert, Icon } from '@edx/paragon'; - -const StatusAlert = (props) => { - const { - alertType, - className, - iconClassName, - title, - message, - dismissible, - onClose, - } = props; - - return ( - - {iconClassName && ( -
    - -
    - )} -
    - {title && ( - {title} - )} - {message} -
    - - )} - onClose={onClose} - open - /> - ); -}; - -StatusAlert.propTypes = { - alertType: PropTypes.string.isRequired, - message: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired, - className: PropTypes.string, - iconClassName: PropTypes.string, - title: PropTypes.string, - dismissible: PropTypes.bool, - onClose: PropTypes.func, -}; - -StatusAlert.defaultProps = { - className: '', - iconClassName: undefined, - title: null, - dismissible: false, - onClose: () => {}, -}; - -export default StatusAlert; diff --git a/src/components/TableComponent/index.jsx b/src/components/TableComponent/index.jsx index 5a45062feb..767802e0fb 100644 --- a/src/components/TableComponent/index.jsx +++ b/src/components/TableComponent/index.jsx @@ -1,166 +1,120 @@ -import React from 'react'; +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useEffect, useCallback } from 'react'; import PropTypes from 'prop-types'; import { sendEnterpriseTrackEvent } from '@edx/frontend-enterprise-utils'; -import { Pagination, Table } from '@edx/paragon'; +import { Pagination, DataTable, Alert } from '@edx/paragon'; +import { Error, WarningFilled } from '@edx/paragon/icons'; import 'font-awesome/css/font-awesome.css'; import TableLoadingSkeleton from './TableLoadingSkeleton'; -import TableLoadingOverlay from '../TableLoadingOverlay'; -import StatusAlert from '../StatusAlert'; import { updateUrl } from '../../utils'; -class TableComponent extends React.Component { - componentDidMount() { - // Get initial data - this.props.paginateTable(); - } - - componentDidUpdate(prevProps) { - const { location } = this.props; - - // Handle the case where the query params have changed. This is used when sorting & paging, but - // also when the back button is used. We need to determine if this is a pagination or sorting - // request as we handle these as slightly different actions in the action handlers. - if (location.search !== prevProps.location.search) { - const prevQueryParams = new URLSearchParams(prevProps.location.search); - const prevPage = prevQueryParams.get('page'); - const prevOrdering = prevQueryParams.get('ordering'); - const currentQueryParams = new URLSearchParams(location.search); - const page = currentQueryParams.get('page'); - const ordering = currentQueryParams.get('ordering'); - if (ordering !== prevOrdering) { - this.props.sortTable(ordering); - } else if (page !== prevPage) { - this.props.paginateTable(parseInt(page, 10)); - } - } - } - - componentWillUnmount() { - this.props.clearTable(); - } - - renderTableContent() { - const { - className, - currentPage, - pageCount, - tableSortable, - data, - ordering, - formatData, - id, - loading, - enterpriseId, - } = this.props; - - const sortByColumn = (column, direction) => { +const TableComponent = ({ + className, currentPage, pageCount, tableSortable, data, formatData, id, loading, enterpriseId, totalCount, + paginateTable, clearTable, columns, error, sortTable, +}) => { + const sortByColumn = useCallback(args => { + const { sortBy } = args; + if (sortBy && sortBy.length > 0) { + const column = sortBy[0]; + const ordering = `${column.desc ? '-' : ''}${column.id}`; updateUrl({ page: 1, - ordering: direction === 'desc' ? `-${column.key}` : column.key, + ordering, }); + sortTable(ordering); sendEnterpriseTrackEvent(enterpriseId, 'edx.ui.enterprise.admin_portal.table.sorted', { tableId: id, - column: column.label, - direction, + column: column.id, + direction: column.desc ? 'desc' : 'asc', }); - }; - - const columnConfig = this.props.columns.map(column => ({ - ...column, - onSort: column.columnSortable ? (direction) => sortByColumn(column, direction) : null, - })); - - let sortDirection; - let sortColumn; - - if (tableSortable) { - sortDirection = ordering && ordering.indexOf('-') !== -1 ? 'desc' : 'asc'; - sortColumn = (ordering && ordering.replace('-', '')) || columnConfig[0].key; } + }, []); - return ( -
    -
    -
    - {loading && } -
    - - - - -
    -
    - { - updateUrl({ page }); - sendEnterpriseTrackEvent(enterpriseId, 'edx.ui.enterprise.admin_portal.table.paginated', { - tableId: id, - page, - }); - }} - /> + useEffect(() => { + // Get initial data + paginateTable(); + return clearTable; + }, []); + + const handlePageChange = (page) => { + updateUrl({ page }); + sendEnterpriseTrackEvent(enterpriseId, 'edx.ui.enterprise.admin_portal.table.paginated', { + tableId: id, + page, + }); + paginateTable(parseInt(page, 10)); + }; + + const renderTableContent = () => ( +
    +
    +
    +
    + + + + +
    + +
    +
    +
    - ); - } - - renderLoadingMessage() { - return ; - } - - renderErrorMessage() { - return ( - - ); - } - - renderEmptyDataMessage() { - return ( - - ); - } - - render() { - const { - data, - loading, - error, - } = this.props; - - return ( - <> - {error && this.renderErrorMessage()} - {loading && !data && this.renderLoadingMessage()} - {!loading && !error && data && data.length === 0 - && this.renderEmptyDataMessage()} - {data && data.length > 0 && this.renderTableContent()} - - ); - } -} +
    + ); + + const renderLoadingMessage = () => ( + + ); + + const renderErrorMessage = () => ( + + Unable to load data +

    {`Try refreshing your screen (${error.message})`}

    +
    + ); + + const renderEmptyDataMessage = () => ( + + There are no results. + + ); + + return ( + <> + {error && renderErrorMessage()} + {loading && !data && renderLoadingMessage()} + {!loading && !error && data && data.length === 0 + && renderEmptyDataMessage()} + {data && data.length > 0 && renderTableContent()} + + ); +}; TableComponent.propTypes = { // Props expected from consumer @@ -173,9 +127,9 @@ TableComponent.propTypes = { // Props expected from TableContainer / redux store enterpriseId: PropTypes.string.isRequired, data: PropTypes.arrayOf(PropTypes.shape({})), + totalCount: PropTypes.number, currentPage: PropTypes.number, pageCount: PropTypes.number, - ordering: PropTypes.string, loading: PropTypes.bool, error: PropTypes.instanceOf(Error), paginateTable: PropTypes.func.isRequired, @@ -190,11 +144,11 @@ TableComponent.defaultProps = { className: null, tableSortable: false, data: undefined, - ordering: undefined, currentPage: undefined, pageCount: undefined, error: null, loading: false, + totalCount: 0, }; export default TableComponent; diff --git a/src/components/analytics/AnalyticsPage.jsx b/src/components/analytics/AnalyticsPage.jsx index 6afe0e3a18..7780721ec6 100644 --- a/src/components/analytics/AnalyticsPage.jsx +++ b/src/components/analytics/AnalyticsPage.jsx @@ -1,47 +1,18 @@ -import React, { useState } from 'react'; +import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; import Hero from '../Hero'; -import StatusAlert from '../StatusAlert'; import AnalyticsCharts from './AnalyticsCharts'; const PAGE_TITLE = 'Analytics'; function AnalyticsPage({ enterpriseId }) { - const [status, setStatus] = useState({ - visible: false, alertType: '', message: '', - }); - - const setSuccessStatus = ({ visible, message = '' }) => { - setStatus({ - visible, - alertType: 'success', - message, - }); - }; - - const renderStatusMessage = () => ( - status && status.visible && ( - setSuccessStatus({ visible: false })} - dismissible - /> - ) - ); - return ( <> -
    - {renderStatusMessage()} -
    ); diff --git a/src/containers/CouponDetails/CouponDetails.test.jsx b/src/containers/CouponDetails/CouponDetails.test.jsx index 1bf4d107d1..d64ffe0e65 100644 --- a/src/containers/CouponDetails/CouponDetails.test.jsx +++ b/src/containers/CouponDetails/CouponDetails.test.jsx @@ -8,9 +8,10 @@ import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { mount } from 'enzyme'; import { render, screen } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; import '@testing-library/jest-dom/extend-expect'; -import { StatusAlert } from '@edx/paragon'; +import { Alert } from '@edx/paragon'; import { SINGLE_USE, MULTI_USE, ONCE_PER_CUSTOMER } from '../../data/constants/coupons'; import EcommerceaApiService from '../../data/services/EcommerceApiService'; @@ -96,10 +97,12 @@ const initialCouponData = { const CouponDetailsWrapper = props => ( - + + + ); @@ -183,8 +186,8 @@ describe('CouponDetails container', () => { }); renderWithRouter(); expect(screen.getByText(COUPON_FILTERS.unassigned.label)).toBeInTheDocument(); - DEFAULT_TABLE_COLUMNS.unassigned.forEach(({ label }) => { - expect(screen.getByText(label)).toBeInTheDocument(); + DEFAULT_TABLE_COLUMNS.unassigned.forEach(({ Header }) => { + expect(screen.getByText(Header)).toBeInTheDocument(); }); }); @@ -203,8 +206,8 @@ describe('CouponDetails container', () => { renderWithRouter(); userEvent.selectOptions(screen.getByLabelText('Filter by code status'), filterType); - DEFAULT_TABLE_COLUMNS[filterType].forEach(({ label }) => { - expect(screen.getByText(label)).toBeInTheDocument(); + DEFAULT_TABLE_COLUMNS[filterType].forEach(({ Header }) => { + expect(screen.getByText(Header)).toBeInTheDocument(); }); }); @@ -338,8 +341,8 @@ describe('CouponDetails container', () => { /> )); - expect(wrapper.find(StatusAlert).prop('alertType')).toEqual('danger'); - wrapper.find(StatusAlert).find('.alert-dialog .btn').simulate('click'); // Retry fetching coupon overview data + expect(wrapper.find(Alert).prop('variant')).toEqual('danger'); + wrapper.find(Alert).find('#try-again').find('.btn').simulate('click'); // Retry fetching coupon overview data expect(spy).toBeCalledTimes(1); expect(spy).toBeCalledWith({ coupon_id: initialCouponData.id }); @@ -390,14 +393,14 @@ describe('CouponDetails container', () => { describe('modals', () => { let spy; - const openModalByActionButton = ({ key }) => { - const actionButton = wrapper.find('table').find('button').find(`.${key}-btn`); + const openModalByActionButton = ({ accessor }) => { + const actionButton = wrapper.find('table').find('button').find(`.${accessor}-btn`); actionButton.simulate('click'); }; - const testModalActionButton = ({ key, label }) => { - const actionButton = wrapper.find('table').find('button').find(`.${key}-btn`); - expect(actionButton.text()).toEqual(label); + const testModalActionButton = ({ accessor, Header }) => { + const actionButton = wrapper.find('table').find('button').find(`.${accessor}-btn`); + expect(actionButton.text()).toEqual(Header); actionButton.simulate('click'); }; @@ -424,22 +427,22 @@ describe('CouponDetails container', () => { it('sets remind modal state on Remind button click', () => { testModalActionButton({ - key: 'remind', - label: 'Remind', + accessor: 'remind', + Header: 'Remind', }); }); it('sets revoke modal state on Revoke button click', () => { testModalActionButton({ - key: 'revoke', - label: 'Revoke', + accessor: 'revoke', + Header: 'Revoke', }); }); it('sets assignment modal state on Assign button click', () => { testModalActionButton({ - key: 'assignment', - label: 'Assign', + accessor: 'assignment', + Header: 'Assign', }); // TODO: The remind/revoke buttons now manage their modal state in their // own components, so we only need to worry about the `assign` action now. @@ -449,8 +452,8 @@ describe('CouponDetails container', () => { it('shows correct remaining uses on assignment modal', () => { testModalActionButton({ - key: 'assignment', - label: 'Assign', + accessor: 'assignment', + Header: 'Assign', }); expect(wrapper.find('.assignment-details .code-remaining-uses').text()).toEqual('Remaining Uses: 85'); @@ -495,8 +498,8 @@ describe('CouponDetails container', () => { it('handles successful code assignment from modal', () => { openModalByActionButton({ - key: 'assignment', - label: 'Assign', + accessor: 'assignment', + Header: 'Assign', }); // fake successful code assignment @@ -504,13 +507,13 @@ describe('CouponDetails container', () => { wrapper.update(); // success status alert - const statusAlert = wrapper.find(StatusAlert); - expect(statusAlert.prop('alertType')).toEqual('success'); + const statusAlert = wrapper.find(Alert); + expect(statusAlert.prop('variant')).toEqual('success'); expect(statusAlert.text()).toContain(SUCCESS_MESSAGES.assign); - statusAlert.find('.alert-dialog .btn').simulate('click'); + statusAlert.find('.btn-tertiary').simulate('click'); // after alert is dismissed - expect(wrapper.find(StatusAlert)).toHaveLength(0); + expect(wrapper.find(Alert)).toHaveLength(0); expect(wrapper.find('CouponDetails').text()).toContain(COUPON_FILTERS.unredeemed.label); // fetches overview data for coupon @@ -520,8 +523,8 @@ describe('CouponDetails container', () => { it('handles successful code revoke from modal', () => { openModalByActionButton({ - key: 'revoke', - label: 'Revoke', + accessor: 'revoke', + Header: 'Revoke', }); // fake successful code assignment @@ -529,8 +532,8 @@ describe('CouponDetails container', () => { wrapper.update(); // success status alert - const statusAlert = wrapper.find(StatusAlert); - expect(statusAlert.prop('alertType')).toEqual('success'); + const statusAlert = wrapper.find(Alert); + expect(statusAlert.prop('variant')).toEqual('success'); expect(statusAlert.text()).toContain(SUCCESS_MESSAGES.revoke); // fetches overview data for coupon @@ -540,8 +543,8 @@ describe('CouponDetails container', () => { it('handles successful code remind from modal', () => { openModalByActionButton({ - key: 'remind', - label: 'Remind', + accessor: 'remind', + Header: 'Remind', }); // fake successful code assignment @@ -549,8 +552,8 @@ describe('CouponDetails container', () => { wrapper.update(); // success status alert - const statusAlert = wrapper.find(StatusAlert); - expect(statusAlert.prop('alertType')).toEqual('success'); + const statusAlert = wrapper.find(Alert); + expect(statusAlert.prop('variant')).toEqual('success'); expect(statusAlert.text()).toContain(SUCCESS_MESSAGES.remind); // does not fetch overview data for coupon @@ -559,8 +562,8 @@ describe('CouponDetails container', () => { it('handles errors in response data for code reminder ', () => { openModalByActionButton({ - key: 'remind', - label: 'Remind', + accessor: 'remind', + Header: 'Remind', }); // fake code assignment 200 status with error in response data. diff --git a/src/containers/LmsConfigurations/LmsConfigurations.test.jsx b/src/containers/LmsConfigurations/LmsConfigurations.test.jsx new file mode 100644 index 0000000000..8a5f01440b --- /dev/null +++ b/src/containers/LmsConfigurations/LmsConfigurations.test.jsx @@ -0,0 +1,85 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import PropTypes from 'prop-types'; +import { MemoryRouter } from 'react-router-dom'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { mount } from 'enzyme'; + +import LmsConfigurations from './index'; +import LmsApiService from '../../data/services/LmsApiService'; + +const mockStore = configureMockStore([thunk]); +jest.mock('./../../data/services/LmsApiService'); +const waitForAsync = () => new Promise(resolve => setImmediate(resolve)); + +const moodleResponse = { + data: { results: [{}] }, +}; +const successFactorsResponse = { + data: { results: [{}] }, +}; + +const canvasResponse = { + data: { results: [{}] }, +}; + +const blackboardResponse = { + data: { results: [{}] }, +}; + +const degreedResponse = { + data: { results: [{}] }, +}; + +const cornerstoneResponse = { + data: { results: [{}] }, +}; + +const initialState = { + portalConfiguration: { + enterpriseId: 'test-enterprise', + contactEmail: 'fake@example.com', + }, +}; + +const LmsConfigurationsWrapper = props => ( + + + + + +); + +LmsConfigurationsWrapper.defaultProps = { + store: mockStore(initialState), +}; + +LmsConfigurationsWrapper.propTypes = { + store: PropTypes.shape({}), +}; + +describe('LmsConfigurationsWrapper', () => { + let spy; + + afterEach(() => { + if (spy) { + spy.mockRestore(); + } + }); + + it('renders moodle collapsable', async () => { + LmsApiService.fetchMoodleConfig.mockResolvedValue(moodleResponse); + LmsApiService.fetchCanvasConfig.mockResolvedValue(canvasResponse); + LmsApiService.fetchBlackboardConfig.mockResolvedValue(blackboardResponse); + LmsApiService.fetchSuccessFactorsConfig.mockResolvedValue(successFactorsResponse); + LmsApiService.fetchDegreedConfig.mockResolvedValue(degreedResponse); + LmsApiService.fetchCornerstoneConfig.mockResolvedValue(cornerstoneResponse); + + const wrapper = mount(); + await waitForAsync(); + expect(wrapper.text()).toContain('Loading...Loading'); + wrapper.update(); + expect(wrapper.text()).toContain('ActiveMoodle'); + }); +}); diff --git a/src/containers/TableContainer/index.jsx b/src/containers/TableContainer/index.jsx index 3c11cbd222..d77e27bcce 100644 --- a/src/containers/TableContainer/index.jsx +++ b/src/containers/TableContainer/index.jsx @@ -14,6 +14,7 @@ const mapStateToProps = (state, ownProps) => { ordering: tableState.ordering, loading: tableState.loading, error: tableState.error, + totalCount: tableState?.data?.count, }; }; diff --git a/src/index.scss b/src/index.scss index 8b689cfd02..e7f2a1fa57 100644 --- a/src/index.scss +++ b/src/index.scss @@ -19,7 +19,6 @@ $fa-font-path: "~font-awesome/fonts"; @import "./components/RequestCodesPage/RequestCodesPage"; @import "./components/Sidebar/Sidebar"; @import "./components/subscriptions/styles/"; -@import "./components/StatusAlert/StatusAlert"; @import "./components/LoadingMessage/LoadingMessage"; @import "./components/TableComponent/TableComponent"; @import "./components/TableLoadingOverlay/TableLoadingOverlay";