Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paragon StatusAlert deprecation #574

Merged
merged 11 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 25 additions & 31 deletions src/profile/AgeMessage.jsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StatusAlert } from '@edx/paragon';
import { Alert } from '@edx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';

function AgeMessage({ accountSettingsUrl }) {
return (
<StatusAlert
alertType="info"
dialog={(
<>
<FormattedMessage
id="profile.age.headline"
defaultMessage="Your profile cannot be shared."
description="error message"
tagName="h6"
/>
<FormattedMessage
id="profile.age.details"
defaultMessage="To share your profile with other {siteName} learners, you must confirm that you are over the age of 13."
description="Error message"
tagName="p"
values={{
siteName: getConfig().SITE_NAME,
}}
/>
<a href={accountSettingsUrl}>
<FormattedMessage
id="profile.age.set.date"
defaultMessage="Set your date of birth"
description="Label on a link to set birthday"
/>
</a>
</>
)}
<Alert
variant="info"
dismissible={false}
open
/>
show
>
<Alert.Heading id="profile.age.headline">
Your profile cannot be shared.
</Alert.Heading>
<FormattedMessage
id="profile.age.details"
defaultMessage="To share your profile with other {siteName} learners, you must confirm that you are over the age of 13."
description="Error message"
tagName="p"
values={{
siteName: getConfig().SITE_NAME,
}}
/>
<Alert.Link href={accountSettingsUrl}>
<FormattedMessage
id="profile.age.set.date"
defaultMessage="Set your date of birth"
description="Label on a link to set birthday"
/>
</Alert.Link>
</Alert>
);
}

Expand Down
6 changes: 4 additions & 2 deletions src/profile/ProfilePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
import { ensureConfig, getConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { StatusAlert, Hyperlink } from '@edx/paragon';
import { Alert, Hyperlink } from '@edx/paragon';

// Actions
import {
Expand Down Expand Up @@ -156,7 +156,9 @@ class ProfilePage extends React.Component {
return (
<div className="row">
<div className="col-md-4 col-lg-3">
<StatusAlert alertType="danger" dialog={photoUploadError.userMessage} dismissible={false} open />
<Alert variant="danger" dismissible={false} show>
{photoUploadError.userMessage}
</Alert>
</div>
</div>
);
Expand Down
45 changes: 45 additions & 0 deletions src/profile/ProfilePage.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,51 @@ describe('<ProfilePage />', () => {
const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot();
});
it('test age message alert', () => {
const storeData = JSON.parse(JSON.stringify(storeMocks.viewOwnProfile));
storeData.userAccount.requiresParentalConsent = true;
storeData.profilePage.account.requiresParentalConsent = true;
const component = (
<AppContext.Provider
value={{
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: { ...getConfig(), COLLECT_YEAR_OF_BIRTH: true },
}}
>
<IntlProvider locale="en">
<Provider store={mockStore(storeData)}>
<ProfilePage {...requiredProfilePageProps} requiresParentalConsent />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
const wrapper = mount(component);
wrapper.update();

expect(wrapper.find('.alert-info').hasClass('show')).toBe(true);
});
it('test photo error alert', () => {
const storeData = JSON.parse(JSON.stringify(storeMocks.viewOwnProfile));
storeData.profilePage.errors.photo = { userMessage: 'error' };
const component = (
<AppContext.Provider
value={{
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: { ...getConfig(), COLLECT_YEAR_OF_BIRTH: true },
}}
>
<IntlProvider locale="en">
<Provider store={mockStore(storeData)}>
<ProfilePage {...requiredProfilePageProps} />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
const wrapper = mount(component);
wrapper.update();

expect(wrapper.find('.alert-danger').hasClass('show')).toBe(true);
});
});

describe('handles analytics', () => {
Expand Down
11 changes: 8 additions & 3 deletions src/profile/forms/SocialLinks.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StatusAlert } from '@edx/paragon';
import { Alert } from '@edx/paragon';
import { connect } from 'react-redux';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons';
Expand Down Expand Up @@ -158,14 +158,19 @@ class SocialLinks extends React.Component {
),
editing: (
<div role="dialog" aria-labelledby="social-links-label">
<form onSubmit={this.handleSubmit}>
<form aria-labelledby="editing-form" onSubmit={this.handleSubmit}>
<EditableItemHeader
headingId="social-links-label"
content={intl.formatMessage(messages['profile.sociallinks.social.links'])}
/>
{/* TODO: Replace this alert with per-field errors. Needs API update. */}
<div id="social-error-feedback">
{error !== null ? <StatusAlert alertType="danger" dialog={error} dismissible={false} open /> : null}
{error !== null
? (
<Alert variant="danger" dismissible={false} show>
{error}
</Alert>
) : null}
</div>
<ul className="list-unstyled">
{socialLinks.map(({ platform, socialLink }) => (
Expand Down
152 changes: 152 additions & 0 deletions src/profile/forms/SocialLinks.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { mount } from 'enzyme';
import PropTypes from 'prop-types';
import React from 'react';
import { Provider } from 'react-redux';
import renderer from 'react-test-renderer';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react';

import SocialLinks from './SocialLinks';
import * as savingEditedBio from '../__mocks__/savingEditedBio.mockStore';
import messages from '../../i18n';

const mockStore = configureMockStore([thunk]);

const defaultProps = {
formId: 'socialLinks',
socialLinks: [
{
platform: 'facebook',
socialLink: 'https://www.facebook.com/aloha',
},
{
platform: 'twitter',
socialLink: 'https://www.twitter.com/ALOHA',
},
],
drafts: {},
visibilitySocialLinks: 'private',
editMode: 'static',
saveState: null,
error: null,
changeHandler: jest.fn(),
submitHandler: jest.fn(),
closeHandler: jest.fn(),
openHandler: jest.fn(),
};

configureI18n({
loggingService: { logError: jest.fn() },
config: {
ENVIRONMENT: 'production',
LANGUAGE_PREFERENCE_COOKIE_NAME: 'yum',
},
messages,
});

const SocialLinksWrapper = props => (
<AppContext.Provider
value={{
authenticatedUser: { userId: null, username: null, administrator: false },
config: getConfig(),
}}
>
<IntlProvider locale="en">
<Provider store={props.store}>
<SocialLinks {...props} />
</Provider>
</IntlProvider>
</AppContext.Provider>
);

SocialLinksWrapper.defaultProps = {
store: mockStore(savingEditedBio),
};

SocialLinksWrapper.propTypes = {
store: PropTypes.shape({}),
};

describe('<SocialLinks />', () => {
['certificates', 'bio', 'goals', 'socialLinks'].forEach(editMode => (
it(`calls social links with edit mode ${editMode}`, () => {
const component = <SocialLinksWrapper {...defaultProps} formId={editMode} />;
const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot();
})
));

it('calls social links with editing', () => {
const changeHandler = jest.fn();
const submitHandler = jest.fn();
const closeHandler = jest.fn();
const component = (
<SocialLinksWrapper
{...defaultProps}
formId="bio"
changeHandler={changeHandler}
submitHandler={submitHandler}
closeHandler={closeHandler}
/>
);
const wrapper = mount(component);
const socialLink = wrapper.find(SocialLinks);
const { platform } = defaultProps.socialLinks[0];
const inputField = socialLink.find(`#social-${platform}`);
inputField.simulate('change', { target: { value: 'test', name: platform } });
expect(changeHandler).toHaveBeenCalledTimes(1);

expect(socialLink.find('#visibilitySocialLinks select').props().value).toBe('private');
const event = { target: { value: 'all_users', name: 'visibilitySocialLinks' } };
socialLink.find('#visibilitySocialLinks select').simulate('change', event);
expect(changeHandler).toHaveBeenCalledTimes(2);

socialLink.find('[aria-labelledby="editing-form"]').simulate('submit');
expect(submitHandler).toHaveBeenCalledTimes(1);

socialLink.find('[aria-labelledby="editing-form"]').find('Button .btn-link').simulate('click');
expect(closeHandler).toHaveBeenCalledTimes(1);
});

it('calls social links with static', () => {
const openHandler = jest.fn();
const component = (
<SocialLinksWrapper
{...defaultProps}
formId="goals"
openHandler={openHandler}
/>
);
const wrapper = mount(component);
const socialLink = wrapper.find(SocialLinks);

socialLink.find('EmptyContent button').first().simulate('click');
expect(openHandler).toHaveBeenCalledTimes(1);
});

it('calls social links with error', () => {
const newStore = JSON.parse(JSON.stringify(savingEditedBio));
newStore.profilePage.errors.bio = { userMessage: 'error' };
const component = (
<AppContext.Provider
value={{
authenticatedUser: { userId: null, username: null, administrator: false },
config: getConfig(),
}}
>
<IntlProvider locale="en">
<Provider store={mockStore(newStore)}>
<SocialLinks {...defaultProps} formId="bio" />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
const wrapper = mount(component);
const socialLink = wrapper.find(SocialLinks);

expect(socialLink.find('.alert-danger').exists()).toBe(true);
});
});
Loading