feat!: add design tokens support (#1737) (#10)

BREAKING CHANGE: Pre-design-tokens theming is no longer supported.

Co-authored-by: Brian Smith <112954497+brian-smith-tcril@users.noreply.github.com>
Co-authored-by: Diana Olarte <diana.olarte@edunext.co>
This commit is contained in:
Nathan Sprenkle
2025-08-15 15:06:21 -04:00
committed by GitHub
parent 0dfbca7cd8
commit 7817ac751c
24 changed files with 1315 additions and 164 deletions

View File

@@ -9,8 +9,8 @@
height: 100%;
max-width: none;
margin: 0;
border-top: 1px solid $light-300;
z-index: $zindex-modal; // Bootstrap's z-index layer for Modals.
border-top: 1px solid var(--pgn-color-light-300);
z-index: var(--pgn-elevation-modal-zindex); // Bootstrap's z-index layer for Modals.
&__form {
position: relative;
@@ -47,7 +47,7 @@
&__results-summary {
font-size: .9rem;
color: $gray-500;
color: var(--pgn-color-gray-500);
padding: 1rem 0 .5rem;
}
@@ -62,7 +62,7 @@
margin-top: 1.5rem;
&__empty {
color: $gray-500;
color: var(--pgn-color-gray-500);
padding: 6rem 0;
text-align: center;
}
@@ -76,17 +76,17 @@
&:hover {
text-decoration: none;
background: $light-300;
background: var(--pgn-color-light-300);
}
&:not(:first-child) {
border-top: 1px solid $light-300;
border-top: 1px solid var(--pgn-color-light-300);
}
}
&__icon {
padding: 0.375rem 0 0 0.375rem;
color: $gray-300;
color: var(--pgn-color-gray-300);
}
&__info {
@@ -99,7 +99,7 @@
align-items: center;
line-height: 2.5;
font-size: 0.875rem;
color: $black;
color: var(--pgn-color-black);
> span {
display: block;
@@ -113,7 +113,7 @@
font-variant-numeric: lining-nums tabular-nums;
min-width: 1.25rem;
line-height: 1rem;
background: $light-300;
background: var(--pgn-color-light-300);
border-radius: 99rem;
font-style: normal;
margin-left: 0.375rem;
@@ -125,7 +125,7 @@
&__breadcrumbs {
display: flex;
gap: 1.25rem;
color: $gray-500;
color: var(--pgn-color-gray-500);
overflow: hidden;
list-style: none;
padding: 0;
@@ -156,14 +156,14 @@
}
.courseware-search-results-tabs {
border-bottom-color: $gray-400 !important;
border-bottom-color: var(--pgn-color-gray-400) !important;
&.nav-tabs .nav-link.active {
border-bottom-width: 4px !important;
}
}
@media (min-width: map-get($grid-breakpoints, 'md')) {
@media (--pgn-size-breakpoint-min-width-md) {
.courseware-search {
&__close {
right: -2.5rem;

View File

@@ -1,22 +1,18 @@
@import "~@edx/brand/paragon/variables";
@import "~@openedx/paragon/scss/core/core";
@import "~@edx/brand/paragon/overrides";
.flag-button {
background-color: $white;
border: 1px solid $light-400;
background-color: var(--pgn-color-white);
border: 1px solid var(--pgn-color-light-400);
border-radius: .2rem;
box-shadow: 0 0 0 2px $light-400;
box-shadow: 0 0 0 2px var(--pgn-color-light-400);
&:hover {
border: 1px solid $primary-300;
box-shadow: 0 0 0 2px $white;
border: 1px solid var(--pgn-color-primary-300);
box-shadow: 0 0 0 2px var(--pgn-color-white);
}
}
.flag-button-selected {
border: 1px solid $primary-300;
box-shadow: 0 0 0 2px $primary-300;
border: 1px solid var(--pgn-color-primary-300);
box-shadow: 0 0 0 2px var(--pgn-color-primary-300);
pointer-events: none;
}

View File

@@ -1,10 +1,10 @@
.outline-sidebar-proctoring-panel {
border: 1px solid $dark-500;
border-top: 5px solid $brand-600;
border: 1px solid var(--pgn-color-dark-500);
border-top: 5px solid var(--pgn-color-brand-600);
}
.proctoring-onboarding-success {
border-top: 5px solid $primary-500;
border-top: 5px solid var(--pgn-color-primary-500);
}
.proctoring-onboarding-submitted {
border-top: 5px solid $dark-500;
border-top: 5px solid var(--pgn-color-dark-500);
}

View File

@@ -7,18 +7,18 @@
.donut-chart-label {
font: {
family: $font-family-sans-serif;
family: var(--pgn-typography-font-family-sans-serif);
size: .2rem;
weight: $font-weight-normal;
weight: var(--pgn-typography-font-weight-normal);
}
text-anchor: middle;
}
.donut-chart-number {
font: {
family: $font-family-monospace;
family: var(--pgn-typography-font-family-monospace);
size: .5rem;
weight: $font-weight-bold;
weight: var(--pgn-typography-font-weight-bold);
}
line-height: 1rem;
text-anchor: middle;
@@ -29,7 +29,7 @@
}
.donut-chart-text {
fill: $primary-500;
fill: var(--pgn-color-primary-500);
-moz-transform: translateY(0.25em);
-ms-transform: translateY(0.25em);
-webkit-transform: translateY(0.25em);
@@ -56,7 +56,7 @@
.donut-ring, .donut-segment, .donut-hole {
&.complete-stroke {
stroke: $info-500;
stroke: var(--pgn-color-info-500);
}
&.divider-stroke {
@@ -65,10 +65,10 @@
}
&.incomplete-stroke {
stroke: $light-300;
stroke: var(--pgn-color-light-300);
}
&.locked-stroke {
stroke: $primary-500;
stroke: var(--pgn-color-primary-500);
}
}

View File

@@ -4,24 +4,24 @@
}
.grade-bar__base {
fill: $light-300;
fill: var(--pgn-color-light-300);
}
.grade-bar__divider {
fill: $primary-500;
fill: var(--pgn-color-primary-500);
width: 1px;
}
.grade-bar--passing {
fill: $primary-500;
fill: var(--pgn-color-primary-500);
}
.grade-bar--current-passing {
fill: $success-500;
fill: var(--pgn-color-success-500);
}
.grade-bar--current-non-passing {
fill: $accent-b;
fill: var(--pgn-color-accent-b);
}
}
@@ -31,22 +31,22 @@
#minimum-grade-tooltip {
.arrow::after {
border-bottom-color: $primary-500;
border-bottom-color: var(--pgn-color-primary-500);
}
}
#passing-grade-tooltip {
background: $success-500;
background: var(--pgn-color-success-500);
.arrow::after {
border-top-color: $success-500;
border-top-color: var(--pgn-color-success-500);
}
}
#non-passing-grade-tooltip {
background: $accent-b;
background: var(--pgn-color-accent-b);
.arrow::after {
border-top-color: $accent-b;
border-top-color: var(--pgn-color-accent-b);
}
}

View File

@@ -5,13 +5,13 @@
.nav a,
.nav button {
&:hover {
background-color: $light-400;
background-color: var(--pgn-color-light-400);
}
}
.nav a {
&:not(.active):hover {
background-color: $light-400;
background-color: var(--pgn-color-light-400);
border-bottom: none;
}
}

View File

@@ -22,7 +22,6 @@
justify-content: center;
button {
@extend .btn-primary;
font-size: 1.2rem;
width: 50%;
}

View File

@@ -149,7 +149,7 @@ const Calculator = () => {
/>
</li>
</ul>
<table className="table small">
<table className="pgn__data-table small">
<thead>
<tr>
<th scope="col">

View File

@@ -4,4 +4,19 @@
background-color: #f1f1f1;
box-shadow: 0 -1px 0 0 #ddd;
}
table {
tr {
border-bottom: var(--pgn-size-border-width) solid var(--pgn-color-border);
}
thead tr {
border-bottom: calc(2 * var(--pgn-size-border-width)) solid var(--pgn-color-border);
border-top: var(--pgn-size-border-width) solid var(--pgn-color-border);
}
tbody tr {
vertical-align: top;
}
}
}

View File

@@ -8,8 +8,8 @@
display: inline-block;
position: relative;
z-index: 2;
background-color: #f1f1f1;
border: solid 1px #ddd;
background-color: #f1f1f1 !important;
border: solid 1px #ddd !important;
border-bottom: none;
border-top-left-radius: .3rem;
border-top-right-radius: .3rem;

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { ErrorPage } from '@edx/frontend-platform/react';
import { StrictDict } from '@edx/react-unit-test-utils';
import { ModalDialog, Modal } from '@openedx/paragon';
import { ModalDialog } from '@openedx/paragon';
import { ContentIFrameLoaderSlot } from '../../../../plugin-slots/ContentIFrameLoaderSlot';
import * as hooks from './hooks';
@@ -65,21 +65,6 @@ const ContentIFrame = ({
onLoad: handleIFrameLoad,
};
let modalContent;
if (modalOptions.isOpen) {
modalContent = modalOptions.body
? <div className="unit-modal">{ modalOptions.body }</div>
: (
<iframe
title={modalOptions.title}
allow={IFRAME_FEATURE_POLICY}
frameBorder="0"
src={modalOptions.url}
style={{ width: '100%', height: modalOptions.height }}
/>
);
}
return (
<>
{(shouldShowContent && !hasLoaded) && (
@@ -90,29 +75,30 @@ const ContentIFrame = ({
<iframe title={title} {...contentIFrameProps} data-testid={testIDs.contentIFrame} />
</div>
)}
{modalOptions.isOpen && (modalOptions.isFullscreen
? (
{modalOptions.isOpen
&& (
<ModalDialog
dialogClassName="modal-lti"
onClose={handleModalClose}
size="fullscreen"
size={modalOptions.isFullscreen ? 'fullscreen' : 'md'}
isOpen
hasCloseButton={false}
>
<ModalDialog.Body className={modalOptions.modalBodyClassName}>
{modalContent}
{modalOptions.body
? <div className="unit-modal">{ modalOptions.body }</div>
: (
<iframe
title={modalOptions.title}
allow={IFRAME_FEATURE_POLICY}
frameBorder="0"
src={modalOptions.url}
style={{ width: '100%', height: modalOptions.height }}
/>
)}
</ModalDialog.Body>
</ModalDialog>
) : (
<Modal
body={modalContent}
dialogClassName="modal-lti"
onClose={handleModalClose}
open
/>
)
)}
)}
</>
);
};

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { ErrorPage } from '@edx/frontend-platform/react';
import { ModalDialog, Modal } from '@openedx/paragon';
import { ModalDialog } from '@openedx/paragon';
import { shallow } from '@edx/react-unit-test-utils';
import PageLoading from '@src/generic/PageLoading';
@@ -14,7 +14,6 @@ jest.mock('@edx/frontend-platform/react', () => ({ ErrorPage: 'ErrorPage' }));
jest.mock('@openedx/paragon', () => jest.requireActual('@edx/react-unit-test-utils')
.mockComponents({
Modal: 'Modal',
ModalDialog: {
Body: 'ModalDialog.Body',
},
@@ -139,7 +138,7 @@ describe('ContentIFrame Component', () => {
});
it('does not display modal if modalOptions returns isOpen: false', () => {
el = shallow(<ContentIFrame {...props} />);
expect(el.instance.findByType(Modal).length).toEqual(0);
expect(el.instance.findByType(ModalDialog).length).toEqual(0);
});
describe('if modalOptions.isOpen', () => {
const testModalOpenAndHandleClose = () => {
@@ -194,10 +193,11 @@ describe('ContentIFrame Component', () => {
beforeEach(() => {
hooks.useModalIFrameData.mockReturnValueOnce({ ...modalIFrameData, modalOptions: modalOptions.withBody });
el = shallow(<ContentIFrame {...props} />);
[component] = el.instance.findByType(Modal);
[component] = el.instance.findByType(ModalDialog);
});
it('displays Modal with div wrapping provided body content if modal.body is provided', () => {
expect(component.props.body).toEqual(<div className="unit-modal">{modalOptions.withBody.body}</div>);
const content = component.findByType(ModalDialog.Body)[0].children[0];
expect(content.matches(shallow(<div className="unit-modal">{modalOptions.withBody.body}</div>))).toEqual(true);
});
testModalOpenAndHandleClose();
});
@@ -205,11 +205,12 @@ describe('ContentIFrame Component', () => {
beforeEach(() => {
hooks.useModalIFrameData.mockReturnValueOnce({ ...modalIFrameData, modalOptions: modalOptions.withUrl });
el = shallow(<ContentIFrame {...props} />);
[component] = el.instance.findByType(Modal);
[component] = el.instance.findByType(ModalDialog);
});
testModalOpenAndHandleClose();
it('displays Modal with iframe to provided url if modal.body is not provided', () => {
expect(component.props.body).toEqual(
const content = component.findByType(ModalDialog.Body)[0].children[0];
expect(content.matches(shallow(
<iframe
title={modalOptions.withUrl.title}
allow={IFRAME_FEATURE_POLICY}
@@ -217,7 +218,7 @@ describe('ContentIFrame Component', () => {
src={modalOptions.withUrl.url}
style={{ width: '100%', height: modalOptions.withUrl.height }}
/>,
);
))).toEqual(true);
});
});
});

View File

@@ -4,7 +4,7 @@
}
.lock-paywall-container svg {
color: $primary-700;
color: var(--pgn-color-primary-700);
}
@media only screen and (min-width: 992px) and (max-width: 1100px) {

View File

@@ -1,5 +1,5 @@
#course-sidebar {
@media (max-width: map-get($grid-breakpoints, "lg")) {
@media (--pgn-size-breakpoint-max-width-lg) {
overflow-y: scroll;
padding: 0 .625rem !important;
}

View File

@@ -6,7 +6,7 @@
}
.outline-sidebar {
@media (min-width: map-get($grid-breakpoints, "xl")) {
@media (--pgn-size-breakpoint-min-width-xl) {
left: 0;
top: 0;
}
@@ -23,12 +23,12 @@
}
.outline-sidebar-heading {
font-weight: $font-weight-bold;
font-weight: var(--pgn-typography-font-weight-bold);
}
}
.course-sidebar-section {
background: $white;
background: var(--pgn-color-white);
border: 1px solid #d7d3d1;
button {
@@ -52,7 +52,7 @@
#outline-sidebar-outline {
margin-top: -1px;
@media (min-width: map-get($grid-breakpoints, "xl")) {
@media (--pgn-size-breakpoint-min-width-xl) {
margin-bottom: 0;
}
@@ -62,14 +62,14 @@
.collapsible-trigger {
border-radius: 0;
padding: map-get($spacers, 3\.5) map-get($spacers, 4) map-get($spacers, 3\.5) map-get($spacers, 5);
padding: var(--pgn-spacing-spacer-3-5) var(--pgn-spacing-spacer-4) var(--pgn-spacing-spacer-3-5) var(--pgn-spacing-spacer-5);
@media (max-width: map-get($grid-breakpoints, "sm")) {
padding-left: map-get($spacers, 4);
@media (--pgn-size-breakpoint-max-width-sm) {
padding-left: var(--pgn-spacing-spacer-4);
}
&:hover {
background-color: $light-500;
background-color: var(--pgn-color-light-500);
}
.collapsible-icon {
@@ -78,7 +78,7 @@
}
&:last-child .pgn_collapsible {
@extend .mb-0;
margin-bottom: 0px !important;
}
}
@@ -86,15 +86,15 @@
padding: 0;
ol li > a {
padding: map-get($spacers, 3\.5) map-get($spacers, 4) map-get($spacers, 3\.5) map-get($spacers, 5\.5);
padding: var(--pgn-spacing-spacer-3-5) var(--pgn-spacing-spacer-4) var(--pgn-spacing-spacer-3-5) var(--pgn-spacing-spacer-5-5);
@media (max-width: map-get($grid-breakpoints, "sm")) {
padding-left: map-get($spacers, 4\.5);
@media (--pgn-size-breakpoint-max-width-sm) {
padding-left: var(--pgn-spacing-spacer-4-5);
}
&:hover {
text-decoration: none;
background-color: $light-500;
background-color: var(--pgn-color-light-500);
}
}
}

View File

@@ -1,5 +1,5 @@
.discussions-sidebar-frame {
@media (max-width: map-get($grid-breakpoints, "xl")) {
@media (--pgn-size-breakpoint-max-width-xl) {
max-height: calc(100vh - 65px);
}
}

View File

@@ -8,6 +8,6 @@
}
.upsell-bullet a {
color: $primary-500;
color: var(--pgn-color-primary-500);
}

View File

@@ -1,7 +1,4 @@
@import "~@edx/brand/paragon/fonts";
@import "~@edx/brand/paragon/variables";
@import "~@openedx/paragon/scss/core/core";
@import "~@edx/brand/paragon/overrides";
@use "@openedx/paragon/styles/css/core/custom-media-breakpoints" as paragonCustomMediaBreakpoints;
@import "~@edx/frontend-component-footer/dist/footer";
@import "~@edx/frontend-component-header/dist/index";
@@ -51,7 +48,7 @@
.nav-link {
border-bottom: 4px solid transparent;
border-top: 4px solid transparent;
color: $gray-700;
color: var(--pgn-color-gray-700);
// temporary until we can remove .btn class from dropdowns
border-left: 0;
@@ -61,9 +58,9 @@
&:hover,
&:focus,
&.active {
font-weight: $font-weight-normal;
color: $primary-500;
border-bottom-color: $primary-500;
font-weight: var(--pgn-typography-font-weight-normal);
color: var(--pgn-color-primary-500);
border-bottom-color: var(--pgn-color-primary-500);
}
}
}
@@ -82,7 +79,7 @@
}
.sequence {
@media (min-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-min-width-sm) {
border: solid 1px #eaeaea;
border-radius: 4px;
}
@@ -94,7 +91,7 @@
}
.notification-btn {
@media (max-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-max-width-xs) {
height: 3rem;
}
}
@@ -103,15 +100,15 @@
display: flex;
flex-grow: 1;
@media (max-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-max-width-xs) {
max-width: 100%;
}
@media (min-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-min-width-sm) {
margin: -1px -1px 0;
}
@media (max-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-max-width-xs) {
width: 100% !important;
}
@@ -127,13 +124,13 @@
height: 3rem;
justify-content: center;
align-items: center;
color: $gray-500;
color: var(--pgn-color-gray-500);
white-space: nowrap;
&:hover,
&:focus,
&.active {
color: $gray-700;
color: var(--pgn-color-gray-700);
}
&:focus {
@@ -148,13 +145,13 @@
left: 0;
right: 0;
height: 2px;
background: $primary;
background: var(--pgn-color-primary-base);
}
}
&.complete {
background-color: #eef7e5;
color: $success;
color: var(--pgn-color-success-base);
}
&:first-child {
@@ -218,12 +215,12 @@
min-width: 0;
margin: 0 1rem;
text-overflow: ellipsis;
color: $gray-700;
color: var(--pgn-color-gray-700);
}
&.active {
.unit-icon {
color: $primary-500;
color: var(--pgn-color-primary-500);
}
&:after {
@@ -235,7 +232,7 @@
right: auto;
width: 2px;
height: auto;
background: $primary;
background: var(--pgn-color-primary-base);
}
}
}
@@ -250,18 +247,18 @@
.previous-btn,
.next-btn {
border: 1px solid $light-400 !important;
color: $gray-700;
border: 1px solid var(--pgn-color-light-400) !important;
color: var(--pgn-color-gray-700);
display: inline-flex;
justify-content: center;
align-items: center;
@media (max-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-max-width-sm) {
padding-top: 1rem;
padding-bottom: 1rem;
}
@media (min-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-min-width-sm) {
min-width: fit-content;
padding-left: 2rem;
padding-right: 2rem;
@@ -272,7 +269,7 @@
border-left-width: 0;
margin-left: 0;
@media (min-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-min-width-sm) {
border-left-width: 1px;
border-top-left-radius: 4px;
}
@@ -282,7 +279,7 @@
border-left-width: 1px;
border-right-width: 0;
@media (min-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-min-width-sm) {
border-top-right-radius: 4px;
border-right-width: 1px;
}
@@ -296,9 +293,9 @@
margin-left: auto;
margin-right: auto;
@media (min-width: map-get($grid-breakpoints, "sm")) {
padding-left: $grid-gutter-width;
padding-right: $grid-gutter-width;
@media (--pgn-size-breakpoint-min-width-sm) {
padding-left: var(--pgn-spacing-grid-gutter-width);
padding-right: var(--pgn-spacing-grid-gutter-width);
}
@media (min-width: 830px) {
@@ -316,8 +313,8 @@
// here we compensate for the padding of the parent div with "container-xl"
// class to ensure that the viewport width is the same as the width of the
// iframe.
margin-left: -$grid-gutter-width * .5;
margin-right: -$grid-gutter-width * .5;
margin-left: calc(var(--pgn-spacing-grid-gutter-width) * -0.5);
margin-right: calc(var(--pgn-spacing-grid-gutter-width) * -0.5);
margin-bottom: 2rem;
@@ -339,9 +336,9 @@
max-width: 640px;
margin: 0 auto;
@media (max-width: map-get($grid-breakpoints, "sm")) {
@media (--pgn-size-breakpoint-max-width-xs) {
flex-direction: column;
gap: $spacer;
gap: var(--pgn-spacing-spacer-base);
}
.previous-button,
@@ -411,8 +408,8 @@
.icon-hover {
&:hover {
color: $primary-500 !important;
background-color: $light-300 !important;
color: var(--pgn-color-primary-500) !important;
background-color: var(--pgn-color-light-300) !important;
}
}
@@ -435,7 +432,7 @@
height: 56px !important;
}
@include media-breakpoint-down(xs) {
@media (--pgn-size-breakpoint-max-width-xs) {
.course-outline-tab .pgn__card {
.pgn__card-header {
display: block;

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Input } from '@openedx/paragon';
import { Form } from '@openedx/paragon';
import { MasqueradeStatus, Payload } from './data/api';
import messages from './messages';
@@ -40,11 +40,10 @@ export const MasqueradeUserNameInput: React.FC<Props> = ({ onSubmit, onError, ..
}, [handleSubmit]);
return (
<Input
<Form.Control
aria-labelledby="masquerade-search-label"
label={intl.formatMessage(messages.userNameLabel)}
onKeyPress={handleKeyPress}
type="text"
{...otherProps}
/>
);

View File

@@ -1,3 +1,3 @@
[dir="rtl"] .new-user-tour-dialog .pgn__modal-hero .pgn__modal-hero-bg {
transform: scaleX(-1);
}
}