diff --git a/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/block.json b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/block.json
new file mode 100644
index 000000000..0c366bf42
--- /dev/null
+++ b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/block.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 2,
+ "name": "wporg-learn/lesson-facilitator-notes",
+ "version": "0.1.0",
+ "title": "Lesson Facilitator Notes",
+ "category": "widgets",
+ "icon": "text",
+ "description": "Shows the facilitator notes for a lesson",
+ "usesContext": [],
+ "attributes": {
+ "lessonPlanId": {
+ "type": "number",
+ "default": null
+ },
+ "lessonPlanContent": {
+ "type": "string",
+ "default": ""
+ },
+ "lessonPlanTitle": {
+ "type": "string",
+ "default": ""
+ }
+ },
+ "textdomain": "wporg-learn",
+ "editorScript": "file:./index.js",
+ "viewScript": "file:./view.js",
+ "style": "file:./style-index.css"
+}
diff --git a/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/index.js b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/index.js
new file mode 100644
index 000000000..3c9a9f32f
--- /dev/null
+++ b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/index.js
@@ -0,0 +1,166 @@
+/**
+ * WordPress dependencies
+ */
+import { registerBlockType } from '@wordpress/blocks';
+import { RichText, useBlockProps } from '@wordpress/block-editor';
+import { Button, ComboboxControl, Icon, Spinner } from '@wordpress/components';
+import { check } from '@wordpress/icons';
+import { useEffect, useState } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import apiFetch from '@wordpress/api-fetch';
+import { debounce } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+import metadata from './block.json';
+import './style.scss';
+
+registerBlockType( metadata.name, {
+ edit: function Edit( { attributes, setAttributes } ) {
+ const { lessonPlanId, lessonPlanContent, lessonPlanTitle } = attributes;
+ const [ searchResults, setSearchResults ] = useState( [] );
+ const [ isExpanded, setIsExpanded ] = useState( false );
+ const [ isSaving, setIsSaving ] = useState( false );
+ const [ saveSuccess, setSaveSuccess ] = useState( null );
+ const [ errorMessage, setErrorMessage ] = useState( null );
+
+ useEffect( () => {
+ // Fetch the initial lesson plan options if lessonPlanId is set
+ if ( lessonPlanId ) {
+ apiFetch( {
+ path: `/wp/v2/lesson-plan/${ lessonPlanId }`,
+ } ).then( ( plan ) => {
+ setSearchResults( [
+ {
+ value: plan.id,
+ label: plan.title.rendered,
+ },
+ ] );
+ } );
+ }
+ }, [] );
+
+ const fetchLessonPlanContent = ( id ) => {
+ apiFetch( { path: `/wp/v2/lesson-plan/${ id }` } ).then( ( plan ) => {
+ const cleanedContent = plan.content.rendered.replace( /\s+/g, ' ' ).trim();
+ setAttributes( {
+ lessonPlanContent: cleanedContent,
+ lessonPlanTitle: plan.title.rendered,
+ } );
+ } );
+ };
+
+ const fetchLessonPlans = debounce( ( searchTerm ) => {
+ apiFetch( {
+ path: `/wp/v2/lesson-plan?search=${ encodeURIComponent( searchTerm ) }&per_page=10`,
+ } ).then( ( plans ) => {
+ if ( plans.length === 0 ) {
+ return;
+ }
+ const options = plans.map( ( plan ) => ( {
+ value: plan.id,
+ label: plan.title.rendered,
+ } ) );
+ setSearchResults( options );
+ } );
+ }, 300 );
+
+ const saveLessonPlanContent = () => {
+ setIsSaving( true );
+ setSaveSuccess( null );
+ apiFetch( {
+ path: `/wp/v2/lesson-plan/${ lessonPlanId }`,
+ method: 'POST',
+ data: {
+ content: lessonPlanContent,
+ title: lessonPlanTitle,
+ },
+ } )
+ .then( () => {
+ setIsSaving( false );
+ setSaveSuccess( true );
+ setTimeout( () => setSaveSuccess( null ), 2000 );
+ } )
+ .catch( ( error ) => {
+ setIsSaving( false );
+ setSaveSuccess( false );
+ setErrorMessage( error.message || __( 'An error occurred while saving', 'wporg-learn' ) );
+ } );
+ };
+
+ const getSaveButton = () => {
+ if ( isSaving ) {
+ return
;
+ }
+ if ( saveSuccess === true ) {
+ return
;
+ }
+ if ( saveSuccess === false ) {
+ return errorMessage;
+ }
+ return __( 'Save Changes', 'wporg-learn' );
+ };
+
+ const getSaveButtonClassName = () => {
+ if ( saveSuccess === true ) {
+ return 'is-success';
+ }
+ if ( saveSuccess === false ) {
+ return 'is-failure';
+ }
+ };
+
+ return (
+
+ {
+ if ( inputValue ) {
+ fetchLessonPlans( inputValue );
+ }
+ } }
+ onChange={ ( newValue ) => {
+ setAttributes( { lessonPlanId: newValue } );
+ if ( newValue ) {
+ fetchLessonPlanContent( newValue );
+ }
+ } }
+ placeholder={ __( 'Search for a lesson plan…', 'wporg-learn' ) }
+ />
+ { lessonPlanId && (
+ <>
+ setAttributes( { lessonPlanTitle: newTitle } ) }
+ />
+
+ setAttributes( { lessonPlanContent: newContent } ) }
+ />
+ >
+ ) }
+ { lessonPlanId && (
+
+ ) }
+
+ );
+ },
+ save: () => null,
+} );
diff --git a/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/index.php b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/index.php
new file mode 100644
index 000000000..5ed22db23
--- /dev/null
+++ b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/index.php
@@ -0,0 +1,71 @@
+ __NAMESPACE__ . '\render',
+ )
+ );
+}
+
+/**
+ * Render the block content.
+ *
+ * @param array $attributes Block attributes.
+ * @param string $content Block default content.
+ * @param WP_Block $block Block instance.
+ *
+ * @throws Error If the script asset file is not readable.
+ * @return string Returns the block markup.
+ */
+function render( $attributes, $content, $block ) {
+ $script_asset_path = get_stylesheet_directory() . '/build/lesson-facilitator-notes/index.asset.php';
+ if ( ! is_readable( $script_asset_path ) ) {
+ throw new Error(
+ 'You need to run `yarn start` or `yarn run build` for the "wporg-learn/lesson-facilitator-notes" block first.'
+ );
+ }
+
+ if ( empty( $attributes['lessonPlanId'] ) ) {
+ return;
+ }
+
+ $lesson_plan = get_post( $attributes['lessonPlanId'] );
+
+ if ( ! $lesson_plan ) {
+ return;
+ }
+
+ $lesson_plan_url = get_permalink( $lesson_plan->ID );
+ return sprintf(
+ // If changing classname here, you'd also need to update it in style.scss and view.js.
+ '
',
+ esc_url( $lesson_plan_url ),
+ esc_html__( 'Facilitator Note', 'wporg-learn' )
+ );
+}
diff --git a/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/style.scss b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/style.scss
new file mode 100644
index 000000000..a07c60357
--- /dev/null
+++ b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/style.scss
@@ -0,0 +1,50 @@
+.wp-block-wporg-learn-lesson-facilitator-notes {
+ .components-base-control__field {
+ margin-bottom: var(--wp--preset--spacing--10);
+ }
+
+ .block-editor-rich-text__editable {
+ white-space: unset !important;
+ overflow: scroll;
+ border: 1px solid var(--wp--preset--color--charcoal-5);
+ margin: var(--wp--preset--spacing--10) 0;
+ padding: var(--wp--preset--spacing--10);
+
+ &.is-expanded {
+ max-height: none;
+ }
+
+ &.is-collapsed {
+ max-height: 300px;
+ }
+ }
+
+ h1.block-editor-rich-text__editable {
+ padding: 0 var(--wp--preset--spacing--10);
+ }
+
+ button {
+ min-width: 118px;
+ justify-content: center;
+
+ &.is-success {
+ background-color: var(--wp--custom--color--green-50) !important;
+
+ &:hover {
+ opacity: 0.9;
+ }
+ }
+
+ &.is-failure {
+ background-color: var(--wp--preset--color--vivid-red) !important;
+
+ &:hover {
+ opacity: 0.9;
+ }
+ }
+ }
+
+ .components-spinner {
+ margin-top: 0;
+ }
+}
diff --git a/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/view.js b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/view.js
new file mode 100644
index 000000000..8ea878560
--- /dev/null
+++ b/wp-content/themes/pub/wporg-learn-2024/src/lesson-facilitator-notes/view.js
@@ -0,0 +1,23 @@
+document.addEventListener( 'DOMContentLoaded', function () {
+ const facilitatorNotes = document.querySelector( '.wporg-learn-lesson-facilitator-notes-label' );
+ const exitCourse = document.querySelector( '.wp-block-sensei-lms-exit-course' );
+ const exitLesson = document.querySelector( '.wp-block-sensei-lms-exit-lesson' );
+ const sidebarToggle = document.querySelector( '.wporg-learn-lesson-sidebar-toggle-wrapper' );
+
+ // for desktop view
+ if ( facilitatorNotes && exitCourse ) {
+ exitCourse.insertAdjacentElement( 'beforebegin', facilitatorNotes.cloneNode( true ) );
+ }
+
+ // for mobile view
+ if ( facilitatorNotes && sidebarToggle ) {
+ sidebarToggle.insertAdjacentElement( 'beforebegin', facilitatorNotes.cloneNode( true ) );
+ }
+
+ // for standalone lesson
+ if ( facilitatorNotes && exitLesson ) {
+ exitLesson.insertAdjacentElement( 'beforebegin', facilitatorNotes.cloneNode( true ) );
+ }
+
+ facilitatorNotes.remove();
+} );
diff --git a/wp-content/themes/pub/wporg-learn-2024/src/style/_sensei.scss b/wp-content/themes/pub/wporg-learn-2024/src/style/_sensei.scss
index ad9f1862d..2a9662441 100644
--- a/wp-content/themes/pub/wporg-learn-2024/src/style/_sensei.scss
+++ b/wp-content/themes/pub/wporg-learn-2024/src/style/_sensei.scss
@@ -14,30 +14,127 @@ body.sensei {
--sensei-module-lesson-color: var(--wp--preset--color--charcoal-1);
--sensei-lm-mobile-header-height: 60px;
+ @media (max-width: 782px) {
+ // The inner progress bar causes a 1px jitter when the sidebar is opened.
+ &.sensei-course-theme--sidebar-open {
+ .wporg-learn-lesson-header-mobile-view {
+ position: relative;
+ top: 1px;
+
+ .wporg-learn-lesson-sidebar-toggle-wrapper {
+ background-color: var(--wp--preset--color--light-grey-2) !important;
+
+ > button {
+ transform: rotate(180deg) scale(1.5) !important;
+ }
+ }
+ }
+
+ .sensei-course-theme__sidebar {
+ top: calc(var(--sensei-lm-header-height) * 2 + var(--sensei-wpadminbar-offset, 0px)) !important;
+ background-color: var(--wp--preset--color--light-grey-2);
+ }
+ }
+ }
+
.wp-block-sensei-lms-course-theme-notices:empty {
display: none;
}
+ // general styles
+ .wporg-learn-lesson-facilitator-notes-label {
+ cursor: pointer;
+ font-family: var(--wp--preset--font-family--inter);
+ font-size: var(--wp--preset--font-size--small);
+ color: var(--wp--custom--link--color--text);
+ text-decoration: underline;
+ text-underline-offset: 0.13em;
+ user-select: none;
+ line-height: 1.1875;
+
+ &:hover {
+ text-decoration: none;
+ }
+
+ @media (max-width: 782px) {
+ display: none;
+ }
+ }
+
.sensei-course-theme-header-content {
> .wp-block-group {
- row-gap: 0;
+ gap: 0;
}
.sensei-course-theme__header__info {
- gap: var(--wp--preset--spacing--30);
+ gap: 60px;
+
+ .wp-block-sensei-lms-exit-course {
+ text-decoration: none;
+ opacity: 1;
+
+ @media (min-width: 783px) {
+ &::before {
+ content: "";
+ display: inline-block;
+ height: var(--sensei-lm-header-height);
+ border-right: 1px solid var(--sensei-course-progress-bar-color);
+ position: absolute;
+ margin-left: -30px;
+ top: 0;
+ }
+ }
+ }
}
- .wp-block-sensei-lms-exit-course {
- margin-left: var(--wp--preset--spacing--30);
+ @media (max-width: 782px) {
+ .sensei-course-theme__sidebar-toggle {
+ display: none !important;
+ }
+ }
- &::before {
- content: "";
- display: inline-block;
- height: 100%;
- border-right: 1px solid var(--sensei-course-progress-bar-color);
- position: absolute;
- margin-left: -30px;
- top: 0;
+ ~ .wporg-learn-lesson-header-mobile-view {
+ display: none;
+
+ @media (max-width: 782px) {
+ display: flex;
+ height: var(--sensei-lm-header-height);
+ padding: 0 0 0 var(--wp--preset--spacing--30);
+
+ .sensei-course-theme-course-progress {
+ display: block;
+ opacity: 1;
+ position: unset;
+
+ @media (max-width: 460px) {
+ width: 47%;
+ }
+ }
+
+ .wporg-learn-lesson-facilitator-notes-label {
+ // mobile styles
+ position: absolute;
+ right: calc(60px + var(--wp--preset--spacing--20));
+ display: block;
+ }
+
+ .wporg-learn-lesson-sidebar-toggle-wrapper {
+ position: absolute;
+ right: 0;
+
+ > button {
+ background: url(../../assets/icon-chevron-down.svg) !important;
+ background-repeat: no-repeat !important;
+ background-position: center !important;
+ transform: scale(1.5);
+ width: 60px;
+ height: 60px;
+ }
+
+ svg {
+ display: none;
+ }
+ }
}
}
}
@@ -58,9 +155,15 @@ body.sensei {
.sensei-course-theme__columns {
margin-top: calc(var(--sensei-lm-header-height) + var(--sensei-wpadminbar-offset, 0px)) !important;
+ @media (max-width: 782px) {
+ margin-top: calc(var(--sensei-lm-header-height) * 2 + var(--sensei-wpadminbar-offset, 0px)) !important;
+ }
+
.sensei-course-theme__sidebar ~ .sensei-course-theme__main-content {
--sensei-lm-sidebar-width: calc(280px + (var(--wp--preset--spacing--edge-space) * 2) - 24px);
margin-top: 0;
+ padding-left: var(--wp--preset--spacing--30);
+ padding-right: var(--wp--preset--spacing--30);
> * {
margin-left: unset;
@@ -181,6 +284,8 @@ body.sensei {
.sensei-course-theme__sidebar {
row-gap: 40px;
+ padding-left: var(--wp--preset--spacing--30) !important;
+ padding-right: var(--wp--preset--spacing--30) !important;
.sensei-lms-course-navigation__modules {
gap: var(--wp--preset--spacing--20);
@@ -750,15 +855,32 @@ body.sensei {
.sensei-course-theme__header--standalone {
.sensei-course-theme-header-content {
border-bottom: 1px solid var(--wp--custom--color--border);
+ gap: 0;
+
+ .wporg-site-breadcrumbs__wrapper {
+ white-space: pre-wrap;
+ }
- div:nth-of-type(2)::before {
- content: "";
- display: inline-block;
- height: 100%;
- border-right: 1px solid var(--sensei-course-progress-bar-color);
- position: absolute;
- margin-left: -30px;
- top: 0;
+ // standalone lesson styles
+ .wporg-learn-lesson-facilitator-notes-label {
+ line-height: var(--wp--custom--body--small--typography--line-height);
+ margin-right: var(--wp--preset--spacing--30) !important;
+ margin-left: auto !important;
+ display: block;
+ }
+
+ @media (min-width: 783px) {
+ .wp-block-sensei-lms-exit-lesson {
+ height: 100%;
+
+ &::before {
+ content: "";
+ display: inline-block;
+ height: 100%;
+ border-right: 1px solid var(--sensei-course-progress-bar-color);
+ margin-right: var(--wp--preset--spacing--30);
+ }
+ }
}
}