feat: responsive behavior

This commit is contained in:
Ben Warzeski
2022-09-09 01:31:23 -04:00
parent d8637f9464
commit 1d9608a755
41 changed files with 478 additions and 324 deletions

View File

@@ -5,6 +5,12 @@
.pgn__card-image-cap {
border-bottom-left-radius: 0 !important;
}
.pgn__card-header-content {
margin-top: 1.5rem;
}
.course-card-content-vertical {
}
}
.course-card-banners {
@@ -13,4 +19,4 @@
border-top-right-radius: 0;
}
}
}
}

View File

@@ -1,6 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CourseCard component snapshot 1`] = `
exports[`CourseCard component snapshot: collapsed 1`] = `
<div
className="mb-4.5 course-card"
data-testid="CourseCard"
>
<Card
orientation="vertical"
>
<div
className="d-flex flex-column w-100"
>
<CourseCardContent
cardId="test-card-id"
orientation="vertical"
/>
<div
className="course-card-banners"
data-testid="CourseCardBanners"
>
<CourseCardBanners
cardId="test-card-id"
/>
</div>
</div>
</Card>
</div>
`;
exports[`CourseCard component snapshot: not collapsed 1`] = `
<div
className="mb-4.5 course-card"
data-testid="CourseCard"
@@ -14,63 +42,16 @@ exports[`CourseCard component snapshot 1`] = `
<div
className="d-flex"
>
<Card.ImageCap
src="hooks.bannerUrl"
srcAlt={
Object {
"formatted": Object {
"defaultMessage": "Course thumbnail",
"description": "Course card banner alt-text",
"id": "learner-dash.courseCard.bannerAlt",
},
}
}
<CourseCardContent
cardId="test-card-id"
orientation="horizontal"
/>
<Card.Body>
<Card.Header
actions={
<CourseCardMenu
cardId="test-card-id"
/>
}
title={
<span
data-testid="CourseCardTitle"
>
hooks.title
</span>
}
/>
<Card.Section>
<CourseCardDetails
cardId="test-card-id"
/>
</Card.Section>
<Card.Footer
orientation="vertical"
textElement={
<RelatedProgramsBadge
cardId="test-card-id"
/>
}
>
<CourseCardActions
cardId="test-card-id"
/>
</Card.Footer>
</Card.Body>
</div>
<div
className="course-card-banners"
data-testid="CourseCardBanners"
>
<CourseBanner
cardId="test-card-id"
/>
<EntitlementBanner
cardId="test-card-id"
/>
<CertificateBanner
<CourseCardBanners
cardId="test-card-id"
/>
</div>

View File

@@ -1,3 +0,0 @@
export { default as CourseBanner } from './CourseBanner';
export { default as CertificateBanner } from './CertificateBanner';
export { default as EntitlementBanner } from './EntitlementBanner';

View File

@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`CourseCard Actions component does not render secondary button if null is returned for secondary props 1`] = `
<div
<ActionRow
data-test-id="CourseCardActions"
>
<Button
@@ -11,11 +11,11 @@ exports[`CourseCard Actions component does not render secondary button if null i
>
primary-children
</Button>
</div>
</ActionRow>
`;
exports[`CourseCard Actions component loads primary and secondary button props from hook 1`] = `
<div
<ActionRow
data-test-id="CourseCardActions"
>
<Button
@@ -32,5 +32,5 @@ exports[`CourseCard Actions component loads primary and secondary button props f
>
primary-children
</Button>
</div>
</ActionRow>
`;

View File

@@ -1,19 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@edx/paragon';
import { ActionRow, Button } from '@edx/paragon';
import useCardActionData from './hooks';
export const CourseCardActions = ({ cardId }) => {
const { primary, secondary } = useCardActionData({ cardId });
return (
<div data-test-id="CourseCardActions">
<ActionRow data-test-id="CourseCardActions">
{(secondary !== null) && (
<Button {...secondary} />
)}
<Button {...primary} />
</div>
</ActionRow>
);
};
CourseCardActions.propTypes = {

View File

@@ -0,0 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
import { hooks as appHooks } from 'data/redux';
import CourseBanner from './CourseBanner';
import CertificateBanner from './CertificateBanner';
import EntitlementBanner from './EntitlementBanner';
export const CourseCardBanners = ({ cardId }) => {
const { isEnrolled } = appHooks.useCardEnrollmentData(cardId);
return (
<div className="course-card-banners" data-testid="CourseCardBanners">
<CourseBanner cardId={cardId} />
<EntitlementBanner cardId={cardId} />
{isEnrolled && <CertificateBanner cardId={cardId} />}
</div>
);
};
CourseCardBanners.propTypes = {
cardId: PropTypes.string.isRequired,
};
export default CourseCardBanners;

View File

@@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
// import PropTypes from 'prop-types';
import { Card } from '@edx/paragon';
import { hooks as appHooks } from 'data/redux';
import RelatedProgramsBadge from './RelatedProgramsBadge';
import CourseCardMenu from './CourseCardMenu';
import messages from '../messages';
import CourseCardActions from './CourseCardActions';
import CourseCardDetails from './CourseCardDetails';
export const CourseCardContent = ({ cardId, orientation }) => {
const { formatMessage } = useIntl();
const { title, bannerUrl } = appHooks.useCardCourseData(cardId);
return (
<>
<Card.ImageCap
src={bannerUrl}
srcAlt={formatMessage(messages.bannerAlt)}
/>
<Card.Body>
<Card.Header
title={<span data-testid="CourseCardTitle">{title}</span>}
actions={<CourseCardMenu cardId={cardId} />}
/>
<Card.Section className="pt-0">
<CourseCardDetails cardId={cardId} />
</Card.Section>
{orientation === 'vertical'
? (
<>
<RelatedProgramsBadge cardId={cardId} />
<Card.Footer orientation="horizontal">
<CourseCardActions cardId={cardId} />
</Card.Footer>
</>
) : (
<Card.Footer
orientation="vertical"
textElement={<RelatedProgramsBadge cardId={cardId} />}
>
<CourseCardActions cardId={cardId} />
</Card.Footer>
)}
</Card.Body>
</>
);
};
CourseCardContent.propTypes = {
cardId: PropTypes.string.isRequired,
orientation: PropTypes.string.isRequired,
};
CourseCardContent.defaultProps = {};
export default CourseCardContent;

View File

@@ -2,6 +2,7 @@
exports[`CourseCard Details component does not have change session button on regular course 1`] = `
<span
className="small"
data-testid="CourseCardDetails"
>
provider-name
@@ -13,6 +14,7 @@ exports[`CourseCard Details component does not have change session button on reg
exports[`CourseCard Details component has change session button on entitlement course 1`] = `
<span
className="small"
data-testid="CourseCardDetails"
>
provider-name

View File

@@ -22,7 +22,7 @@ export const CourseCardDetails = ({ cardId }) => {
} = useCardDetailsData({ cardId, dispatch });
return (
<span data-testid="CourseCardDetails">
<span className="small" data-testid="CourseCardDetails">
{providerName} {courseNumber}
{!(isEntitlement && !isFulfilled) && (
<>

View File

@@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Card } from '@edx/paragon';
import { useIsCollapsed } from '../hooks';
import CourseCardBanners from './CourseCardBanners';
import CourseCardContent from './CourseCardContent';
export const CourseCardLayout = ({
cardId,
}) => {
const isCollapsed = useIsCollapsed();
return (
<div className="mb-4.5 course-card" data-testid="CourseCard">
<Card orientation={isCollapsed ? 'vertical' : 'horizontal'}>
<div className="d-flex flex-column w-100">
<div className="d-flex">
<CourseCardContent cardId={cardId} />
</div>
<div className="course-card-banners" data-testid="CourseCardBanners">
<CourseCardBanners cardId={cardId} />
</div>
</div>
</Card>
</div>
);
};
CourseCardLayout.propTypes = {
cardId: PropTypes.string.isRequired,
};
export default CourseCardLayout;

View File

@@ -3,6 +3,7 @@
exports[`RelatedProgramsBadge component snapshot: 3 programs 1`] = `
<Fragment>
<Button
className="pr-0 mr-0"
data-testid="RelatedProgramsBadge"
onClick={[MockFunction useRelatedProgramsBadge.openModal]}
size="sm"

View File

@@ -20,6 +20,7 @@ export const RelatedProgramsBadge = ({ cardId }) => {
<>
<Button
data-testid="RelatedProgramsBadge"
className="pr-0 mr-0"
variant="tertiary"
size="sm"
iconBefore={Program}

View File

@@ -1,6 +1,12 @@
import { useIntl } from '@edx/frontend-platform/i18n';
import { useWindowSize, breakpoints } from '@edx/paragon';
import { hooks as appHooks } from 'data/redux';
export const useIsCollapsed = () => {
const { width } = useWindowSize();
return width < breakpoints.small.maxWidth;
};
export const useCardData = ({ cardId }) => {
const { formatMessage } = useIntl();
const { title, bannerUrl } = appHooks.useCardCourseData(cardId);

View File

@@ -4,68 +4,39 @@ import PropTypes from 'prop-types';
// import PropTypes from 'prop-types';
import { Card } from '@edx/paragon';
import useCardData from './hooks';
import RelatedProgramsBadge from './components/RelatedProgramsBadge';
import CourseCardMenu from './components/CourseCardMenu';
import {
CourseBanner,
CertificateBanner,
EntitlementBanner,
} from './components/Banners';
import CourseCardActions from './components/CourseCardActions';
import messages from './messages';
import CourseCardDetails from './components/CourseCardDetails';
import { useIsCollapsed } from './hooks';
import CourseCardContent from './components/CourseCardContent';
import CourseCardBanners from './components/CourseCardBanners';
import './CourseCard.scss';
export const CourseCard = ({ cardId }) => {
const {
isEnrolled, title, bannerUrl, formatMessage,
} = useCardData({
cardId,
});
export const CourseCard = ({
cardId,
}) => {
const isCollapsed = useIsCollapsed();
const orientation = isCollapsed ? 'vertical' : 'horizontal';
return (
<div className="mb-4.5 course-card" data-testid="CourseCard">
<Card orientation="horizontal">
<Card orientation={orientation}>
<div className="d-flex flex-column w-100">
<div className="d-flex">
<Card.ImageCap
src={bannerUrl}
srcAlt={formatMessage(messages.bannerAlt)}
/>
<Card.Body>
<Card.Header
title={<span data-testid="CourseCardTitle">{title}</span>}
actions={<CourseCardMenu cardId={cardId} />}
/>
<Card.Section>
<CourseCardDetails cardId={cardId} />
</Card.Section>
<Card.Footer
orientation="vertical"
textElement={<RelatedProgramsBadge cardId={cardId} />}
>
<CourseCardActions cardId={cardId} />
</Card.Footer>
</Card.Body>
</div>
{isCollapsed
? (
<CourseCardContent cardId={cardId} orientation={orientation} />
) : (
<div className="d-flex">
<CourseCardContent cardId={cardId} orientation={orientation} />
</div>
)}
<div className="course-card-banners" data-testid="CourseCardBanners">
<CourseBanner cardId={cardId} />
<EntitlementBanner cardId={cardId} />
{isEnrolled && <CertificateBanner cardId={cardId} />}
<CourseCardBanners cardId={cardId} />
</div>
</div>
</Card>
</div>
);
};
CourseCard.propTypes = {
cardId: PropTypes.string.isRequired,
};
CourseCard.defaultProps = {};
export default CourseCard;

View File

@@ -5,36 +5,21 @@ import CourseCard from '.';
import hooks from './hooks';
jest.mock('./hooks', () => ({
__esModule: true,
default: jest.fn(),
useIsCollapsed: jest.fn(),
}));
jest.mock('./components/RelatedProgramsBadge', () => 'RelatedProgramsBadge');
jest.mock('./components/CourseCardMenu', () => 'CourseCardMenu');
jest.mock('./components/Banners', () => ({
CourseBanner: () => 'CourseBanner',
CertificateBanner: () => 'CertificateBanner',
EntitlementBanner: () => 'EntitlementBanner',
}));
jest.mock('./components/CourseCardActions', () => 'CourseCardActions');
jest.mock('./components/CourseCardDetails', () => 'CourseCardDetails');
const dataProps = {
title: 'hooks.title',
bannerUrl: 'hooks.bannerUrl',
formatMessage: jest.fn(msg => ({ formatted: msg })),
isEnrolled: true,
};
jest.mock('./components/CourseCardBanners', () => 'CourseCardBanners');
jest.mock('./components/CourseCardContent', () => 'CourseCardContent');
const cardId = 'test-card-id';
describe('CourseCard component', () => {
test('snapshot', () => {
hooks.mockReturnValueOnce(dataProps);
test('snapshot: collapsed', () => {
hooks.useIsCollapsed.mockReturnValueOnce(true);
expect(shallow(<CourseCard cardId={cardId} />)).toMatchSnapshot();
expect(hooks).toHaveBeenCalledWith({ cardId });
});
test('snapshot: not enrolled (no certificate card)', () => {
hooks.mockReturnValueOnce({ ...dataProps, isEnrolled: true });
test('snapshot: not collapsed', () => {
hooks.useIsCollapsed.mockReturnValueOnce(false);
expect(shallow(<CourseCard cardId={cardId} />)).toMatchSnapshot();
});
});

View File

@@ -13,6 +13,8 @@ import CourseCard from 'containers/CourseCard';
import messages from './messages';
import './index.scss';
export const useCourseListData = () => {
const [pageNumber, setPageNumber] = React.useState(1);
const [sortBy, setSortBy] = React.useState(SortKeys.title);

View File

@@ -0,0 +1,4 @@
#course-list-heading-container {
display: flex;
justify-content: space-between;
}

View File

@@ -1,5 +1,6 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import { Container, Col, Row } from '@edx/paragon';
import {
thunkActions,
@@ -25,23 +26,28 @@ export const Dashboard = () => {
const hasAvailableDashboards = appHooks.useHasAvailableDashboards();
const showSelectSessionModal = appHooks.useShowSelectSessionModal();
return (
<div className="d-flex flex-column p-2" id="course-dashboard">
<div id="dashboard-container" className="d-flex flex-column p-2">
{hasAvailableDashboards && <EnterpriseDashboardModal />}
{hasCourses ? (
<>
<div className="d-flex" style={{ margin: 'auto' }}>
<div className="w-100 mw-md mr-4">
<Container fluid size="xl">
<Row>
<Col
xs={{ span: 12, offset: 0 }}
sm={{ span: 8, offset: 2 }}
md={{ span: 12, offset: 0 }}
lg={{ span: 10, offset: 1 }}
xl={{ span: 8, offset: 0 }}
className="p-0 px-4"
>
{showSelectSessionModal && (<SelectSessionModal />)}
<CourseList />
</div>
<div id="dashboard-sidebar-container mw-xs">
</Col>
<Col md={12} xl={4} className="p-0 pr-4 pl-1">
<WidgetSidebar />
</div>
</div>
</>
) : (
<EmptyCourse />
)}
</Col>
</Row>
</Container>
) : (<EmptyCourse />)}
</div>
);
};

View File

@@ -1,4 +0,0 @@
#course-list-heading-container {
display: flex;
justify-content: space-between;
}

View File

@@ -7,6 +7,7 @@ import { AppContext } from '@edx/frontend-platform/react';
import { AvatarButton, Dropdown } from '@edx/paragon';
import { hooks as appHooks } from 'data/redux';
import { useIsCollapsed } from './hooks';
import messages from './messages';
export const AuthenticatedUserDropdown = ({ username }) => {
@@ -14,49 +15,58 @@ export const AuthenticatedUserDropdown = ({ username }) => {
const { authenticatedUser } = React.useContext(AppContext);
const { profileImage } = authenticatedUser;
const dashboard = appHooks.useEnterpriseDashboardData();
console.log({ dashboard });
const isCollapsed = useIsCollapsed();
return (
<>
<Dropdown className="user-dropdown">
<Dropdown.Toggle as={AvatarButton} src={profileImage} id="user" variant="primary">
<span data-hj-suppress className="d-none d-md-inline">
{username}
</span>
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
<Dropdown.Header>SWITCH DASHBOARD</Dropdown.Header>
<Dropdown.Item as="a" href="/edx-dashboard" className="active">Personal</Dropdown.Item>
{!!dashboard && (
<Dropdown.Item
as="a"
href={dashboard.url}
key={dashboard.label}
>
{dashboard.label} {formatMessage(messages.dashboard)}
</Dropdown.Item>
)}
<Dropdown.Divider />
<Dropdown className="user-dropdown">
<Dropdown.Toggle
as={AvatarButton}
src={profileImage}
id="user"
variant="primary"
>
<span data-hj-suppress className="d-none d-md-inline">
{username}
</span>
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
<Dropdown.Header>SWITCH DASHBOARD</Dropdown.Header>
<Dropdown.Item as="a" href="/edx-dashboard" className="active">Personal</Dropdown.Item>
{!!dashboard && (
<Dropdown.Item
as="a"
href={dashboard.url}
key={dashboard.label}
>
{dashboard.label} {formatMessage(messages.dashboard)}
</Dropdown.Item>
)}
<Dropdown.Divider />
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/u/${username}`}>
{formatMessage(messages.profile)}
</Dropdown.Item>
{isCollapsed && (
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/u/${username}`}>
{formatMessage(messages.profile)}
{formatMessage(messages.viewPrograms)}
</Dropdown.Item>
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/account/settings`}>
{formatMessage(messages.account)}
)}
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/account/settings`}>
{formatMessage(messages.account)}
</Dropdown.Item>
{getConfig().ORDER_HISTORY_URL && (
<Dropdown.Item href={getConfig().ORDER_HISTORY_URL}>
{formatMessage(messages.orderHistory)}
</Dropdown.Item>
{getConfig().ORDER_HISTORY_URL && (
<Dropdown.Item href={getConfig().ORDER_HISTORY_URL}>
{formatMessage(messages.orderHistory)}
</Dropdown.Item>
)}
<Dropdown.Item href={getConfig().SUPPORT_URL}>
{formatMessage(messages.help)}
</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item href={getConfig().LOGOUT_URL}>
{formatMessage(messages.signOut)}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</>
)}
<Dropdown.Item href={getConfig().SUPPORT_URL}>
{formatMessage(messages.help)}
</Dropdown.Item>
<Dropdown.Divider />
<Dropdown.Item href={getConfig().LOGOUT_URL}>
{formatMessage(messages.signOut)}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
};

View File

@@ -1,4 +1,6 @@
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
@@ -6,7 +8,7 @@ import { Image } from '@edx/paragon';
import messages from './messages';
export const GreetingBanner = () => {
export const GreetingBanner = ({ size }) => {
let greetMessage;
const hour = new Date().getHours();
@@ -18,20 +20,37 @@ export const GreetingBanner = () => {
greetMessage = messages.goodMorning;
}
const isSmall = size === 'small';
return (
<div className="d-flex p-5 align-items-center justify-content-center">
<div
className={classNames(
'd-flex align-items-center justify-content-center',
{ 'p-5': !isSmall, 'p-3.5': isSmall },
)}
>
<Image
style={{ width: '148px' }}
style={{ width: isSmall ? '46px' : '148px' }}
className="d-block"
src={getConfig().LOGO_WHITE_URL}
alt={getConfig().SITE_NAME}
/>
<div className="greetings-slash-container bg-brand-500" />
<h1 className="text-center text-accent-b">
<FormattedMessage {...greetMessage} />
</h1>
<div className={`greetings-slash-container-${size} bg-brand-500`} />
{isSmall
? (
<h5 className="text-center text-accent-b">
<FormattedMessage {...greetMessage} />
</h5>
) : (
<h1 className="text-center text-accent-b">
<FormattedMessage {...greetMessage} />
</h1>
)}
</div>
);
};
GreetingBanner.propTypes = {
size: PropTypes.oneOf('small', 'large').isRequired,
};
export default GreetingBanner;

View File

@@ -0,0 +1,10 @@
import React from 'react';
import { useWindowSize, breakpoints } from '@edx/paragon';
export const useIsCollapsed = () => {
const { width } = useWindowSize();
const isCollapsed = React.useMemo(() => (width <= breakpoints.large.maxWidth), [width]);
return isCollapsed;
};
export default { useIsCollapsed };

View File

@@ -9,28 +9,40 @@ import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
import GreetingBanner from './GreetingBanner';
import ConfirmEmailBanner from './ConfirmEmailBanner';
import { useIsCollapsed } from './hooks';
import messages from './messages';
import './index.scss';
export const LearnerDashboardHeader = () => {
export const UserMenu = () => {
const { authenticatedUser } = useContext(AppContext);
return authenticatedUser ? (<AuthenticatedUserDropdown username={authenticatedUser.username} />) : null;
};
export const LearnerDashboardHeader = () => {
const { formatMessage } = useIntl();
const isCollapsed = useIsCollapsed();
return (
<div className="d-flex flex-column bg-primary">
<>
<ConfirmEmailBanner />
<header className="learner-dashboard-header">
<div className="d-flex">
<Button variant="inverse-tertiary" iconBefore={Program}>
{formatMessage(messages.switchToProgram)}
</Button>
<div className="flex-grow-1" />
{authenticatedUser && (
<AuthenticatedUserDropdown username={authenticatedUser.username} />
)}
</div>
</header>
<GreetingBanner />
</div>
<div className="flex-column bg-primary">
<header className="learner-dashboard-header">
<div className="d-flex">
{isCollapsed && (<div className="my-auto ml-1"><UserMenu /></div>)}
{(!isCollapsed) && (
<Button variant="inverse-tertiary" iconBefore={Program}>
{formatMessage(messages.switchToProgram)}
</Button>
)}
<div className="flex-grow-1">
{isCollapsed && <GreetingBanner size="small" />}
</div>
{!isCollapsed && (<UserMenu />)}
</div>
</header>
{!isCollapsed && <GreetingBanner size="large" />}
</div>
</>
);
};

View File

@@ -1,4 +1,10 @@
.greetings-slash-container {
.greetings-slash-container-small {
height: 4px;
width: 40px;
transform-origin: center;
transform: rotate(-70deg);
}
.greetings-slash-container-large {
height: 8px;
width: 120px;
transform-origin: center;

View File

@@ -16,6 +16,11 @@ const messages = defineMessages({
defaultMessage: 'Profile',
description: 'The text for the user menu Profile navigation link.',
},
viewPrograms: {
id: 'leanerDashboard.menu.viewPrograms.label',
defaultMessage: 'View Programs',
description: 'The text for the user menu View Programs navigation link.',
},
account: {
id: 'leanerDashboard.menu.account.label',
defaultMessage: 'Account',

View File

@@ -29,49 +29,58 @@ exports[`RelatedProgramsModal snapshot: closed 1`] = `
<p>
Are you looking to expand your knowledge? Enrolling in a Program lets you take a series of courses in the subject that you're interested in
</p>
<CardGrid
columnSizes={
Object {
"lg": 6,
"xlg": 4,
"xs": 12,
}
}
>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program1",
},
"programUrl": "program-1-url",
}
}
key="program-1-url"
/>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program2",
},
"programUrl": "program-2-url",
}
}
key="program-2-url"
/>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program3",
},
"programUrl": "program-3-url",
}
}
key="program-3-url"
/>
</CardGrid>
<Container>
<Row>
<Col
key="program-1-url"
lg={6}
sm={12}
>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program1",
},
"programUrl": "program-1-url",
}
}
/>
</Col>
<Col
key="program-2-url"
lg={6}
sm={12}
>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program2",
},
"programUrl": "program-2-url",
}
}
/>
</Col>
<Col
key="program-3-url"
lg={6}
sm={12}
>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program3",
},
"programUrl": "program-3-url",
}
}
/>
</Col>
</Row>
</Container>
</ModalDialog.Body>
</ModalDialog>
`;
@@ -105,49 +114,58 @@ exports[`RelatedProgramsModal snapshot: open 1`] = `
<p>
Are you looking to expand your knowledge? Enrolling in a Program lets you take a series of courses in the subject that you're interested in
</p>
<CardGrid
columnSizes={
Object {
"lg": 6,
"xlg": 4,
"xs": 12,
}
}
>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program1",
},
"programUrl": "program-1-url",
}
}
key="program-1-url"
/>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program2",
},
"programUrl": "program-2-url",
}
}
key="program-2-url"
/>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program3",
},
"programUrl": "program-3-url",
}
}
key="program-3-url"
/>
</CardGrid>
<Container>
<Row>
<Col
key="program-1-url"
lg={6}
sm={12}
>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program1",
},
"programUrl": "program-1-url",
}
}
/>
</Col>
<Col
key="program-2-url"
lg={6}
sm={12}
>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program2",
},
"programUrl": "program-2-url",
}
}
/>
</Col>
<Col
key="program-3-url"
lg={6}
sm={12}
>
<ProgramCard
data={
Object {
"programData": Object {
"dataFor": "program3",
},
"programUrl": "program-3-url",
}
}
/>
</Col>
</Row>
</Container>
</ModalDialog.Body>
</ModalDialog>
`;

View File

@@ -23,7 +23,7 @@ export const ProgramCard = ({ data }) => {
);
return (
<Card
className="program-card d-inline-block bg-primary-500 text-white pb-3.5"
className="program-card mx-auto bg-primary-500 text-white mb-3.5 pb-3.5"
style={{ width: '18rem', color: 'white' }}
>
<Card.ImageCap

View File

@@ -2,7 +2,7 @@
exports[`RelatedProgramsModal ProgramCard snapshot 1`] = `
<Card
className="program-card d-inline-block bg-primary-500 text-white pb-3.5"
className="program-card mx-auto bg-primary-500 text-white mb-3.5 pb-3.5"
style={
Object {
"color": "white",

View File

@@ -3,7 +3,9 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { CardGrid, ModalDialog } from '@edx/paragon';
import {
Container, Row, Col, ModalDialog,
} from '@edx/paragon';
import ProgramCard from './components/ProgramCard';
import messages from './messages';
@@ -36,13 +38,15 @@ export const RelatedProgramsModal = ({
</ModalDialog.Header>
<ModalDialog.Body className="pl-0 overflow-hidden">
<p>{formatMessage(messages.description)}</p>
<CardGrid
columnSizes={{ lg: 6, xlg: 4, xs: 12 }}
>
{relatedPrograms.map((programData) => (
<ProgramCard key={programData.programUrl} data={programData} />
))}
</CardGrid>
<Container>
<Row>
{relatedPrograms.map((programData) => (
<Col key={programData.programUrl} lg={6} sm={12}>
<ProgramCard data={programData} />
</Col>
))}
</Row>
</Container>
</ModalDialog.Body>
</ModalDialog>
);

View File

@@ -11,26 +11,15 @@ import './index.scss';
export const WidgetSidebar = () => (
<div className="widget-sidebar">
<div className="d-flex">
{/* <img src='more-courses-sidewidget.svg' />
<div>
<h3>
<FormattedMessage {...messages.lookingForChallengePrompt} />
</h3>
<Hyperlink variant='brand' destination='#'>
<FormattedMessage {...messages.findCoursesButton} />
</Hyperlink>
</div> */}
<Card orientation="horizontal" className="mb-4">
<Card orientation="horizontal">
<Card.ImageCap
src={moreCoursesSVG}
srcAlt="course side widget"
/>
<Card.Body className="m-auto pr-2">
<h3>
<h4>
<FormattedMessage {...messages.lookingForChallengePrompt} />
</h3>
</h4>
<Hyperlink variant="brand" destination="#">
<FormattedMessage {...messages.findCoursesButton} />
</Hyperlink>

View File

@@ -1,8 +1,12 @@
@import "@edx/paragon/scss/core/core";
.widget-sidebar {
margin-top: map-get($spacers, 5);
width: 400px;
flex-shrink: 0;
padding-left: map-get($spacers, 2);
padding-right: map-get($spacers, 2);
}
@include media-breakpoint-down(md) {
.widget-sidebar {
display: none;
}
}