Skip to content

Commit

Permalink
fix: fixed responsiveness issues on mobile view (#1133)
Browse files Browse the repository at this point in the history
* fix: fixed reponsiveness issues on mobile view

* test: updated tests

* refactor: refactored code as requested
  • Loading branch information
eemaanamir authored Nov 26, 2024
1 parent c025310 commit c9b3fb9
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 118 deletions.
49 changes: 42 additions & 7 deletions src/profile-v2/CertificateCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { FormattedDate, FormattedMessage, useIntl } from '@edx/frontend-platform
import { Hyperlink } from '@openedx/paragon';
import get from 'lodash.get';

import classNames from 'classnames';
import professionalCertificateSVG from './assets/professional-certificate.svg';
import verifiedCertificateSVG from './assets/verified-certificate.svg';
import messages from './Certificates.messages';
import { useIsOnMobileScreen } from './data/hooks';

const CertificateCard = ({
certificateType,
Expand All @@ -27,6 +29,8 @@ const CertificateCard = ({
audit: null,
}[certificateType] || null;

const isMobileView = useIsOnMobileScreen();

return (
<div
key={`${modifiedDate}-${courseId}`}
Expand All @@ -37,24 +41,47 @@ const CertificateCard = ({
className="certificate-type-illustration"
style={{ backgroundImage: `url(${certificateIllustration})` }}
/>
<div className="d-flex flex-column position-relative p-0 width-19625rem">
<div className={classNames(
'd-flex flex-column position-relative p-0',
{ 'max-width-19rem': isMobileView },
{ 'width-19625rem': !isMobileView },
)}
>
<div className="w-100 color-black">
<p className="small mb-0 font-weight-normal">
<p className={classNames([
'mb-0 font-weight-normal',
isMobileView ? 'x-small' : 'small',
])}
>
{intl.formatMessage(get(
messages,
`profile.certificates.types.${certificateType}`,
messages['profile.certificates.types.unknown'],
))}
</p>
<h4 className="m-0 color-black">{courseDisplayName}</h4>
<p className="small mb-0">
<p className={classNames([
'm-0 color-black',
isMobileView ? 'h5' : 'h4',
])}
>
{courseDisplayName}
</p>
<p className={classNames([
'mb-0',
isMobileView ? 'x-small' : 'small',
])}
>
<FormattedMessage
id="profile.certificate.organization.label"
defaultMessage="From"
/>
</p>
<h5 className="mb-0 color-black">{courseOrganization}</h5>
<p className="small mb-0">
<p className={classNames([
'mb-0',
isMobileView ? 'x-small' : 'small',
])}
>
<FormattedMessage
id="profile.certificate.completion.date.label"
defaultMessage="Completed on {date}"
Expand All @@ -69,12 +96,20 @@ const CertificateCard = ({
destination={downloadUrl}
target="_blank"
showLaunchIcon={false}
className="btn btn-primary btn-rounded font-weight-normal px-4 py-0625rem"
className={classNames(
'btn btn-primary btn-rounded font-weight-normal px-4 py-0625rem',
{ 'btn-sm': isMobileView },
)}
>
{intl.formatMessage(messages['profile.certificates.view.certificate'])}
</Hyperlink>
</div>
<p className="small mb-0 pt-3">
<p
className={classNames([
'mb-0 pt-3',
isMobileView ? 'x-small' : 'small',
])}
>
<FormattedMessage
id="profile.certificate.uuid"
defaultMessage="Credential ID {certificate_uuid}"
Expand Down
107 changes: 67 additions & 40 deletions src/profile-v2/Certificates.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,80 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { connect } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';

import classNames from 'classnames';
import CertificateCard from './CertificateCard';
import { certificatesSelector } from './data/selectors';
import { useIsOnMobileScreen, useIsOnTabletScreen } from './data/hooks';

const Certificates = ({ certificates }) => (
<div>
<div className="col justify-content-start align-items-start g-5rem p-0">
<div className="col align-self-stretch height-2625rem justify-content-start align-items-start p-0">
<h2 className="font-weight-bold text-primary-500 m-0">
<FormattedMessage
id="profile.your.certificates"
defaultMessage="Your certificates"
description="heading for the certificates section"
/>
</h2>
const Certificates = ({ certificates }) => {
const isMobileView = useIsOnMobileScreen();
const isTabletView = useIsOnTabletScreen();
return (
<div>
<div className="col justify-content-start align-items-start g-5rem p-0">
<div className="col align-self-stretch height-2625rem justify-content-start align-items-start p-0">
<p className={classNames([
'font-weight-bold text-primary-500 m-0',
isMobileView ? 'h3' : 'h2',
])}
>
<FormattedMessage
id="profile.your.certificates"
defaultMessage="Your certificates"
description="heading for the certificates section"
/>
</p>
</div>
<div className="col justify-content-start align-items-start pt-2 p-0">
<p className={classNames([
'font-weight-normal text-gray-800 m-0 p-0',
isMobileView ? 'h5' : 'p',
])}
>
<FormattedMessage
id="profile.certificates.description"
defaultMessage="Your learner records information is only visible to you. Only your username is visible to others on {siteName}."
description="description of the certificates section"
values={{
siteName: getConfig().SITE_NAME,
}}
/>
</p>
</div>
</div>
<div className="col justify-content-start align-items-start pt-2 p-0">
<p className="font-weight-normal text-gray-800 m-0 p-0">
{certificates?.length > 0 ? (
<div className="col">
<div className={classNames(
'row align-items-center pt-5 g-3rem',
{ 'justify-content-center': isTabletView },
)}
>
{certificates.map(certificate => (
<CertificateCard
key={certificate.courseId}
certificateType={certificate.certificateType}
courseDisplayName={certificate.courseDisplayName}
courseOrganization={certificate.courseOrganization}
modifiedDate={certificate.modifiedDate}
downloadUrl={certificate.downloadUrl}
courseId={certificate.courseId}
uuid={certificate.uuid}
/>
))}
</div>
</div>
) : (
<div className="pt-5">
<FormattedMessage
id="profile.certificates.description"
defaultMessage="Your learner records information is only visible to you. Only your username is visible to others on {siteName}."
description="description of the certificates section"
values={{
siteName: getConfig().SITE_NAME,
}}
id="profile.no.certificates"
defaultMessage="You don't have any certificates yet."
description="displays when user has no course completion certificates"
/>
</p>
</div>
</div>
{certificates?.length > 0 ? (
<div className="col">
<div className="row align-items-center pt-5 g-3rem">
{certificates.map(certificate => (
<CertificateCard key={certificate.courseId} {...certificate} />
))}
</div>
</div>
) : (
<div className="pt-5">
<FormattedMessage
id="profile.no.certificates"
defaultMessage="You don't have any certificates yet."
description="displays when user has no course completion certificates"
/>
</div>
)}
</div>
);
)}
</div>
);
};

Certificates.propTypes = {
certificates: PropTypes.arrayOf(PropTypes.shape({
Expand Down
63 changes: 55 additions & 8 deletions src/profile-v2/ProfilePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ensureConfig, getConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Alert, Hyperlink } from '@openedx/paragon';
import classNames from 'classnames';
import {
fetchProfile,
saveProfilePhoto,
Expand All @@ -22,6 +23,7 @@ import PageLoading from './PageLoading';
import { profilePageSelector } from './data/selectors';
import messages from './ProfilePage.messages';
import withParams from '../utils/hoc';
import { useIsOnMobileScreen, useIsOnTabletScreen } from './data/hooks';

ensureConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');

Expand All @@ -42,6 +44,8 @@ const ProfilePage = ({ params }) => {
} = useSelector(profilePageSelector);

const [viewMyRecordsUrl, setViewMyRecordsUrl] = useState(null);
const isMobileView = useIsOnMobileScreen();
const isTabletView = useIsOnTabletScreen();

useEffect(() => {
const { CREDENTIALS_BASE_URL } = context.config;
Expand Down Expand Up @@ -81,7 +85,10 @@ const ProfilePage = ({ params }) => {

return (
<Hyperlink
className="btn btn-brand btn-rounded font-weight-normal px-4 py-0625rem text-nowrap"
className={classNames(
'btn btn-brand btn-rounded font-weight-normal px-4 py-0625rem text-nowrap',
{ 'btn-sm': isMobileView },
)}
target="_blank"
showLaunchIcon={false}
destination={viewMyRecordsUrl}
Expand Down Expand Up @@ -109,10 +116,21 @@ const ProfilePage = ({ params }) => {
<PageLoading srMessage={intl.formatMessage(messages['profile.loading'])} />
) : (
<>
<div className="profile-page-bg-banner bg-primary d-md-block align-items-center px-75rem py-4rem h-100 w-100">
<div
className={classNames(
'profile-page-bg-banner bg-primary d-md-block align-items-center py-4rem h-100 w-100',
{ 'px-4.5': isMobileView },
{ 'px-75rem': !isMobileView },
)}
>
<div className="col container-fluid w-100 h-100 bg-white py-0 px-25rem rounded-75">
<div className="col h-100 w-100 py-4 px-0 justify-content-start g-15rem">
<div className="row-auto d-flex flex-wrap align-items-center h-100 w-100 justify-content-start g-15rem">
<div
className={classNames([
'row-auto d-flex flex-wrap align-items-center h-100 w-100 justify-content-start g-15rem',
isMobileView || isTabletView ? 'flex-column' : 'flex-row',
])}
>
<ProfileAvatar
className="col p-0"
src={profileImage.src}
Expand All @@ -122,17 +140,46 @@ const ProfilePage = ({ params }) => {
savePhotoState={savePhotoState}
isEditable={isAuthenticatedUserProfile() && !requiresParentalConsent}
/>
<div className="col justify-content-start align-items-start h-100 w-100 m-0 p-0">
<h1 className="row h3 m-0 font-weight-bold text-truncate text-primary-500">{params.username}</h1>
<div
className={classNames([
'col h-100 w-100 m-0 p-0',
isMobileView || isTabletView
? 'd-flex flex-column justify-content-center align-items-center'
: 'justify-content-start align-items-start',
])}
>
<p className={classNames([
'row m-0 font-weight-bold text-truncate text-primary-500',
isMobileView ? 'h4' : 'h3',
])}
>
{params.username}
</p>
{isBlockVisible(name) && (
<p className="row pt-2 text-gray-800 font-weight-normal m-0">{name}</p>
<p className={classNames([
'row pt-2 text-gray-800 font-weight-normal m-0',
isMobileView ? 'h5' : 'p',
])}
>
{name}
</p>
)}
<div className={classNames(
'row pt-2 m-0',
isMobileView
? 'd-flex justify-content-center align-items-center flex-column'
: 'g-1rem',
)}
<div className="row pt-2 m-0 g-1rem">
>
<DateJoined date={dateJoined} />
<UserCertificateSummary count={courseCertificates.length} />
</div>
</div>
<div className="col-auto p-0">
<div className={classNames([
'p-0 ',
isMobileView || isTabletView ? 'col d-flex justify-content-center' : 'col-auto',
])}
>
{renderViewMyRecordsButton()}
</div>
</div>
Expand Down
33 changes: 21 additions & 12 deletions src/profile-v2/UsernameDescription.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import React from 'react';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import classNames from 'classnames';
import { useIsOnMobileScreen } from './data/hooks';

const UsernameDescription = () => (
<p className="text-gray-800 font-weight-normal m-0">
<FormattedMessage
id="profile.username.description"
defaultMessage="Your learner records information is only visible to you. Only your username is visible to others on {siteName}."
description="A description of the username field"
values={{
siteName: getConfig().SITE_NAME,
}}
/>
</p>
);
const UsernameDescription = () => {
const isMobileView = useIsOnMobileScreen();
return (
<p className={classNames([
'text-gray-800 font-weight-normal m-0',
isMobileView ? 'h5' : 'p',
])}
>
<FormattedMessage
id="profile.username.description"
defaultMessage="Your learner records information is only visible to you. Only your username is visible to others on {siteName}."
description="A description of the username field"
values={{
siteName: getConfig().SITE_NAME,
}}
/>
</p>
);
};

export default UsernameDescription;
Loading

0 comments on commit c9b3fb9

Please sign in to comment.