diff --git a/wp-content/themes/pub/wporg-learn-2024/assets/icon-chevron-down.svg b/wp-content/themes/pub/wporg-learn-2024/assets/icon-chevron-down.svg new file mode 100644 index 000000000..4493213c0 --- /dev/null +++ b/wp-content/themes/pub/wporg-learn-2024/assets/icon-chevron-down.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/wp-content/themes/pub/wporg-learn-2024/functions.php b/wp-content/themes/pub/wporg-learn-2024/functions.php index 9a9526cb1..b7a7377e1 100644 --- a/wp-content/themes/pub/wporg-learn-2024/functions.php +++ b/wp-content/themes/pub/wporg-learn-2024/functions.php @@ -14,6 +14,7 @@ require_once __DIR__ . '/src/learning-pathway-cards/index.php'; require_once __DIR__ . '/src/learning-pathway-header/index.php'; require_once __DIR__ . '/src/lesson-course-info/index.php'; +require_once __DIR__ . '/src/lesson-facilitator-notes/index.php'; require_once __DIR__ . '/src/lesson-grid/index.php'; require_once __DIR__ . '/src/lesson-standalone/index.php'; require_once __DIR__ . '/src/search-results-context/index.php'; @@ -443,11 +444,6 @@ function get_learning_pathway_level_content( $learning_pathway ) { * @return array The modified breadcrumbs. */ function set_site_breadcrumbs( $breadcrumbs ) { - if ( isset( $breadcrumbs[0] ) ) { - // Change the title of the first breadcrumb to 'Home'. - $breadcrumbs[0]['title'] = 'Home'; - } - $post_id = get_the_ID(); $post_type = get_post_type(); diff --git a/wp-content/themes/pub/wporg-learn-2024/patterns/sensei-lesson-header.php b/wp-content/themes/pub/wporg-learn-2024/patterns/sensei-lesson-header.php index 67da9358b..1ed39568a 100644 --- a/wp-content/themes/pub/wporg-learn-2024/patterns/sensei-lesson-header.php +++ b/wp-content/themes/pub/wporg-learn-2024/patterns/sensei-lesson-header.php @@ -42,5 +42,19 @@ + + +
+ + + + + +
+ +
+ +
+ diff --git a/wp-content/themes/pub/wporg-learn-2024/patterns/sensei-lesson-standalone.php b/wp-content/themes/pub/wporg-learn-2024/patterns/sensei-lesson-standalone.php index f4cba0493..827a1fb29 100644 --- a/wp-content/themes/pub/wporg-learn-2024/patterns/sensei-lesson-standalone.php +++ b/wp-content/themes/pub/wporg-learn-2024/patterns/sensei-lesson-standalone.php @@ -15,8 +15,8 @@ - -
+ +

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); + } + } } }