Skip to content

Commit e6febe6

Browse files
DeimerMnavinkarkerapomegranitedrpenidoihor-romaniuk
authored
chore: teak 2 update (#10)
* refactor: remove custom order function from course libraries list (openedx#1865) (openedx#1888) (cherry picked from commit bc18fff) * perf: use Library search results to populate container card preview [FC-0083] [TEAK] (openedx#1889) * fix: several library unit page UX bugs (openedx#1868) * fix: rename "Organize" tab to "Manage" * fix: duplicate key warnings * fix: uniform messages while adding to collection * fix: do not allow units be added to a unit (cherry picked from commit 0fdc460) * perf: use Library search results to populate container card preview (openedx#1820) * fix: use Library search results to populate container card preview * feat: show published children when showing only published Unit content * fix: nits (cherry picked from commit 24e4695) --------- Co-authored-by: Rômulo Penido <romulo.penido@gmail.com> * fix: manage access modal on duplicated xblock (openedx#1874) * fix: unit pages ux bugs [FC-0083] (openedx#1884) (openedx#1916) This PR fixes some UX bugs related to the unit pages: * Sort for "recently modified" on unit tab does not update after adding new components to units * Change component delete warning message It's a backport of openedx#1884 * fix: UX issues in unit page (openedx#1913) (openedx#1923) Fixes the following issues: * Selection behavior * Component selection is by header click only * Newly created blocks within a unit should be selected on creation/save, appear selected, and have their sidebar open * Some long text components seem to display at the default height rather than a longer height * Within the full-page unit view, the "add to collection" overflow menu item on components does not seem to work/only opens the sidebar. * Draft status indicator text is not vertically centered with icon * When reordering, dragging a short component past a long component often causes a strange stutter effect. * When dragging to reorder a component, moving quickly or scrolling often causes the drag handle to be lost / causes the block to jump somewhere else * Reordering may not consistently support a keyboard-accessible option to change order, like in course authoring * Tag button on component header opens the old tag side pane (cherry picked from commit 8c3fab3) * fix: invalidate search results when publishing all changes in library (openedx#1925) (openedx#1927) (cherry picked from commit cdb8016) Co-authored-by: Braden MacDonald <braden@opencraft.com> * fix: improve focus/selected style on library authoring (openedx#1918) (openedx#1930) Improves the focus and selected styles from the LibraryPage and UnitPage. * fix: review/sync bugs [FC-0083] (openedx#1905) (openedx#1941) Fixes issues related to component libraries' review/sync flow * Inconsistent sync pane title versions * Library content shown in preview warning only appears in review changes modal when that modal is opened from the review tab * Some new changes only appear within library review tab on scroll at top of list * Vertically misaligned sync icon in review changes message on course outline * Show available updates whenever content is updated, regardless of number of updates available * fix: Issue with read-only units in libraries & published version of units in library units picker (openedx#1940) Fixes the issues from openedx#1633 (comment) * In successfully added units, the "add new component" widget appears sometimes * In the "add existing unit" modal, the preview shows draft versions of units * fix: search modal refresh on typing (openedx#1938) (openedx#1948) * [Teak] backport openedx#1949, openedx#1999 and openedx#2002 (openedx#2006) * feat: select component and show sidebar on edit (openedx#1949) Select component that is being edited in library and show its sidebar. Also fixes issue with children component listing in library unit page (cherry picked from commit 08ac1c0) * fix: search text flickering (openedx#1999) Fix flickering issue in search field. (cherry picked from commit 6f3b7ab) * feat: open collection or unit page on double click only (openedx#2002) Opens collection or unit page only on double click. (cherry picked from commit 503642b) * fix: change InplaceTextEditor style and add optimistic update (openedx#1953) (openedx#2014) * Optimistic update for renaming Components, Collections and Containers * Change the InplaceTextEditor to show the new text until the onSave promise resolves * Change the InplaceTextEditor style to: Always show the rename button * fix: rename library publish button (openedx#2015) * fix: do open editor of new xblock when duplicating (openedx#2017) * feat: display editors as modals (openedx#1838) * fix: do open editor of new xblock when duplicating (openedx#1887) Fixes bug where after duplicating an xblock, the editor modal of the old xblock is being open instead of the new copied xblock. * [Teak] fix: Inconsistent publish status filter menu placement & fix: Remove never published filter from component picker (openedx#2021) * fix: Inconsistent publish status filter menu placement (openedx#1966) * fix: Remove never published filter from component picker (openedx#1947) Removes the never-published filter option from the component picker and unit picker. * fix: refresh xblock inline after accepting/rejecting library sync (openedx#2022) (openedx#2028) Instead of reloading the entire Unit after syncing changes from the library, just reload the xblock that was changed. (cherry picked from commit ac5574d) * fix: set maxHeight on TextEditor TinyMce widget [FC-0090] (openedx#2024) (openedx#2030) Sets a max_height=500px for the TinyMCE editor when editing a Text/Html component. This prevents the autoresize plugin from expanding the editor textarea beyond the bounds of the editor modal. ⚠️ Because the max height can only be a numeric pixel value, we can't use clever settings like vh or %, and so we're forced to limit the height of the editor to a fixed size for all screen sizes in order to address this issue. (cherry picked from commit c5f7d0c) * fix: upstreamInfo is not always provided (openedx#2041) (openedx#2042) (cherry picked from commit 3fc0f27) * fix: selection card wiggle (openedx#2047) * fix: set unit preview readonly on sidebar (openedx#2008) (openedx#2059) Make the unit preview on the sidebar read-only and add `Truncate` to the `InplaceTextEditor` * fix: backport changes for html button in text component markdown editor (openedx#2065) * fix: markdown editor issues in modal (openedx#2076) This PR resolves rendering issues with the Markdown editor inside the modal. The problem began after a PR [1] introduced the use of modals for the editor. The EditorPage [2] component expects a `isMarkdownEditorEnabledForCourse` prop, which was missing in that implementation. [1] openedx#1838 [2] https://github.com/openedx/frontend-app-authoring/pull/1838/files#diff-147218ef88726880178ea895988a5d3feaf2c0c4459086a8de7a4080cbe37de7R226 Backports openedx#2074 * fix: Expand all now expands subsections (openedx#2085) * fix: files & uploads menu was truncated due to overflow-x (openedx#2071) (openedx#2077) * fix: (backport) remove an extra editing xblock modal on unit page (openedx#2111) (openedx#2130) * fix: (backport) enable markdown editor in libraries (openedx#2098) * fix: enable markdown editor for problems in libraries too This fix is also achieved on master via 5991fd3 / openedx#2068 but this is a simpler fix, not a direct backport of that refactor. * fix: remove duplicate markdown_edited save request (openedx#2127) Removes the unnecessary duplicate save request of markdown_edited value to the backend. Part of: openedx#2099 Backports: 62589ae --------- Co-authored-by: Muhammad Anas <88967643+Anas12091101@users.noreply.github.com> * fix: remove icon and empty breadcrumb from libraries (openedx#2129) (openedx#2133) * fix: (backport) text truncate issue in the search modal (openedx#2151) * [Teak] fix: published name in unit sidebar in container picker & Issues on Inplace Editor (openedx#2140) Backport of fix: show unit published name in sidebar on content picker [FC-0090] openedx#2100 Backport of fix: Issue on the Inplace editor [FC-0090] openedx#2101 * feat: add `v2` `CourseAuthoringUnitSidebarSlot` (openedx#2000) * fix: advanced-settings api should not camel-case return value (backport) (openedx#2087) * fix: advanced-settings api should not camel-case return value (openedx#1581) * fix: update advanced module list not working (openedx#2189) Backend was still expecting `{'advanced_modules', {'value': ['poll', 'problem-builder', 'h5pxblock']}}` but without this change, it was receiving `{'advancedModules', ['poll', 'problem-builder', 'h5pxblock']}` Follow up to openedx#1581 --------- Co-authored-by: Muhammad Faraz Maqsood <fmaqsood@2u.com> * fix: clear selection on files & uploads page after deleting (backport) (openedx#2228) * refactor: remove selected rows when deleting or adding elements * refactor: ensure unique asset IDs when adding new ones * refactor: remove unnecessary loading checks in mockStore function * test: add unit tests for TableActions component * fix: loading unit page directly from link after logging in in Teak (openedx#2246) This is a simple version of the fix for Teak; on master it was fixed with openedx#1867 * fix: pages and resources plugins not rendered (openedx#1885) * docs: (backport) adding comprehensive readme documentation for plugin slots (openedx#2340) * fix: publish btn doesn't show after component edit When we edit & save the component, publish button doesn't show up until we refresh the page manualy or open this unit by opening previous unit and coming back to this unit again. In this commit, we are dispatching a storage event whenever we edit the component, it'll refresh the page & show the publish button as expected. --------- Co-authored-by: Navin Karkera <navin@opencraft.com> Co-authored-by: Jillian <jill@opencraft.com> Co-authored-by: Rômulo Penido <romulo.penido@gmail.com> Co-authored-by: Ihor Romaniuk <ihor.romaniuk@raccoongang.com> Co-authored-by: Braden MacDonald <braden@opencraft.com> Co-authored-by: Chris Chávez <xnpiochv@gmail.com> Co-authored-by: Daniel Valenzuela <dsvalenzuela@uc.cl> Co-authored-by: Tony Busa <70979397+tonybusa@users.noreply.github.com> Co-authored-by: Muhammad Anas <88967643+Anas12091101@users.noreply.github.com> Co-authored-by: Victor Navarro <vm.navarro94@gmail.com> Co-authored-by: diana-villalvazo-wgu <diana.villalvazo@wgu.edu> Co-authored-by: bydawen <oleksandr.buhaienko@raccoongang.com> Co-authored-by: Arunmozhi <tecoholic@users.noreply.github.com> Co-authored-by: José Ignacio Palma <jignaciopm13@gmail.com> Co-authored-by: Muhammad Faraz Maqsood <fmaqsood@2u.com> Co-authored-by: Brayan Cerón <86393372+bra-i-am@users.noreply.github.com> Co-authored-by: Jansen Kantor <jkantor@edx.org> Co-authored-by: Jacobo Dominguez <jacobo.dominguez@wgu.edu> Co-authored-by: Muhammad Faraz Maqsood <faraz.maqsood@A006-01130.local>
1 parent 98dab67 commit e6febe6

21 files changed

Lines changed: 358 additions & 36 deletions

File tree

src/course-unit/CourseUnit.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const CourseUnit = ({ courseId }) => {
4141
courseUnit,
4242
isLoading,
4343
sequenceId,
44+
courseUnitLoadingStatus,
4445
unitTitle,
4546
unitCategory,
4647
errorMessage,
@@ -210,6 +211,7 @@ const CourseUnit = ({ courseId }) => {
210211
courseId={courseId}
211212
blockId={blockId}
212213
isUnitVerticalType={isUnitVerticalType}
214+
courseUnitLoadingStatus={courseUnitLoadingStatus}
213215
unitXBlockActions={unitXBlockActions}
214216
courseVerticalChildren={courseVerticalChildren.children}
215217
handleConfigureSubmit={handleConfigureSubmit}

src/course-unit/course-sequence/hooks.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,19 @@ export function useSequenceNavigationMetadata(courseId, currentSequenceId, curre
1212
const isLastUnit = !nextUrl;
1313
const sequenceIds = useSelector(getSequenceIds);
1414
const sequenceIndex = sequenceIds.indexOf(currentSequenceId);
15-
const unitIndex = sequence.unitIds.indexOf(currentUnitId);
15+
let unitIndex = sequence?.unitIds.indexOf(currentUnitId);
1616

1717
const nextSequenceId = sequenceIndex < sequenceIds.length - 1 ? sequenceIds[sequenceIndex + 1] : null;
1818
const previousSequenceId = sequenceIndex > 0 ? sequenceIds[sequenceIndex - 1] : null;
19-
19+
if (!unitIndex) {
20+
// Handle case where unitIndex is not found
21+
unitIndex = 0;
22+
}
2023
let nextLink;
2124
const nextIndex = unitIndex + 1;
2225

23-
if (nextIndex < sequence.unitIds.length) {
24-
const nextUnitId = sequence.unitIds[nextIndex];
26+
if (nextIndex < sequence?.unitIds.length) {
27+
const nextUnitId = sequence?.unitIds[nextIndex];
2528
nextLink = `/course/${courseId}/container/${nextUnitId}/${currentSequenceId}`;
2629
} else if (nextSequenceId) {
2730
const pathToNextUnit = decodeURIComponent(nextUrl);
@@ -32,7 +35,7 @@ export function useSequenceNavigationMetadata(courseId, currentSequenceId, curre
3235
const previousIndex = unitIndex - 1;
3336

3437
if (previousIndex >= 0) {
35-
const previousUnitId = sequence.unitIds[previousIndex];
38+
const previousUnitId = sequence?.unitIds[previousIndex];
3639
previousLink = `/course/${courseId}/container/${previousUnitId}/${currentSequenceId}`;
3740
} else if (previousSequenceId) {
3841
const pathToPreviousUnit = decodeURIComponent(prevUrl);

src/course-unit/course-sequence/sequence-navigation/SequenceNavigation.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ const SequenceNavigation = ({
3535

3636
const shouldDisplayNotificationTriggerInSequence = useWindowSize().width < breakpoints.small.minWidth;
3737
const renderUnitButtons = () => {
38-
if (sequence.unitIds?.length === 0 || unitId === null) {
38+
if (sequence?.unitIds?.length === 0 || unitId === null) {
3939
return (
4040
<div style={{ flexBasis: '100%', minWidth: 0, borderBottom: 'solid 1px #EAEAEA' }} />
4141
);
4242
}
4343

4444
return (
4545
<SequenceNavigationTabs
46-
unitIds={sequence.unitIds || []}
46+
unitIds={sequence?.unitIds || []}
4747
unitId={unitId}
4848
handleCreateNewCourseXBlock={handleCreateNewCourseXBlock}
4949
showPasteUnit={showPasteUnit}

src/course-unit/data/selectors.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const getCourseVerticalChildren = (state) => state.courseUnit.courseVerti
1616
export const getCourseOutlineInfo = (state) => state.courseUnit.courseOutlineInfo;
1717
export const getCourseOutlineInfoLoadingStatus = (state) => state.courseUnit.courseOutlineInfoLoadingStatus;
1818
export const getMovedXBlockParams = (state) => state.courseUnit.movedXBlockParams;
19-
const getLoadingStatuses = (state) => state.courseUnit.loadingStatus;
19+
export const getLoadingStatuses = (state) => state.courseUnit.loadingStatus;
2020
export const getIsLoading = createSelector(
2121
[getLoadingStatuses],
2222
loadingStatus => Object.values(loadingStatus)

src/course-unit/hooks.jsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
getSavingStatus,
3636
getSequenceStatus,
3737
getStaticFileNotices,
38+
getLoadingStatuses,
3839
} from './data/selectors';
3940
import {
4041
changeEditTitleFormOpen,
@@ -51,6 +52,7 @@ export const useCourseUnit = ({ courseId, blockId }) => {
5152
const [isMoveModalOpen, openMoveModal, closeMoveModal] = useToggle(false);
5253

5354
const courseUnit = useSelector(getCourseUnitData);
55+
const courseUnitLoadingStatus = useSelector(getLoadingStatuses);
5456
const savingStatus = useSelector(getSavingStatus);
5557
const isLoading = useSelector(getIsLoading);
5658
const errorMessage = useSelector(getErrorMessage);
@@ -215,9 +217,28 @@ export const useCourseUnit = ({ courseId, blockId }) => {
215217
}
216218
}, [isMoveModalOpen]);
217219

220+
useEffect(() => {
221+
const handlePageRefreshUsingStorage = (event) => {
222+
// ignoring tests for if block, because it triggers when someone
223+
// edits the component using editor which has a separate store
224+
/* istanbul ignore next */
225+
if (event.key === 'courseRefreshTriggerOnComponentEditSave') {
226+
dispatch(fetchCourseSectionVerticalData(blockId, sequenceId));
227+
dispatch(fetchCourseVerticalChildrenData(blockId, isSplitTestType));
228+
localStorage.removeItem(event.key);
229+
}
230+
};
231+
232+
window.addEventListener('storage', handlePageRefreshUsingStorage);
233+
return () => {
234+
window.removeEventListener('storage', handlePageRefreshUsingStorage);
235+
};
236+
}, [blockId, sequenceId, isSplitTestType]);
237+
218238
return {
219239
sequenceId,
220240
courseUnit,
241+
courseUnitLoadingStatus,
221242
unitTitle,
222243
unitCategory,
223244
errorMessage,

src/course-unit/sidebar/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,4 @@ export const getIconVariant = (visibilityState, published, hasChanges) => {
9999
* @param {string} id - The course unit ID.
100100
* @returns {string} The clear course unit ID extracted from the provided data.
101101
*/
102-
export const extractCourseUnitId = (id) => id.match(/block@(.+)$/)[1];
102+
export const extractCourseUnitId = (id) => id?.match(/block@(.+)$/)[1];

src/course-unit/xblock-container-iframe/index.tsx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,16 @@ import { useIframeContent } from '../../generic/hooks/useIframeContent';
3737
import { useIframeMessages } from '../../generic/hooks/useIframeMessages';
3838
import VideoSelectorPage from '../../editors/VideoSelectorPage';
3939
import EditorPage from '../../editors/EditorPage';
40+
import { RequestStatus } from '../../data/constants';
4041

4142
const XBlockContainerIframe: FC<XBlockContainerIframeProps> = ({
42-
courseId, blockId, unitXBlockActions, courseVerticalChildren, handleConfigureSubmit, isUnitVerticalType,
43+
courseId,
44+
blockId,
45+
unitXBlockActions,
46+
courseVerticalChildren,
47+
handleConfigureSubmit,
48+
isUnitVerticalType,
49+
courseUnitLoadingStatus,
4350
}) => {
4451
const intl = useIntl();
4552
const dispatch = useDispatch();
@@ -70,6 +77,23 @@ const XBlockContainerIframe: FC<XBlockContainerIframeProps> = ({
7077
setIframeRef(iframeRef);
7178
}, [setIframeRef]);
7279

80+
useEffect(() => {
81+
const iframe = iframeRef?.current;
82+
if (!iframe) { return undefined; }
83+
84+
const handleIframeLoad = () => {
85+
if (courseUnitLoadingStatus.fetchUnitLoadingStatus === RequestStatus.FAILED) {
86+
window.location.reload();
87+
}
88+
};
89+
90+
iframe.addEventListener('load', handleIframeLoad);
91+
92+
return () => {
93+
iframe.removeEventListener('load', handleIframeLoad);
94+
};
95+
}, [iframeRef]);
96+
7397
const onXBlockSave = useCallback(/* istanbul ignore next */ () => {
7498
closeXBlockEditorModal();
7599
closeVideoSelectorModal();

src/course-unit/xblock-container-iframe/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ export interface XBlockContainerIframeProps {
4242
courseId: string;
4343
blockId: string;
4444
isUnitVerticalType: boolean,
45+
courseUnitLoadingStatus: {
46+
fetchUnitLoadingStatus: string;
47+
fetchVerticalChildrenLoadingStatus: string;
48+
fetchXBlockDataLoadingStatus: string;
49+
};
4550
unitXBlockActions: {
4651
handleDelete: (XBlockId: string | null) => void;
4752
handleDuplicate: (XBlockId: string | null) => void;

src/editors/data/redux/thunkActions/app.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,16 @@ export const saveBlock = (content, returnToUnit) => (dispatch) => {
125125
content,
126126
onSuccess: (response) => {
127127
dispatch(actions.app.setSaveResponse(response));
128+
const parsedData = JSON.parse(response.config.data);
129+
if (parsedData?.has_changes) {
130+
const storageKey = 'courseRefreshTriggerOnComponentEditSave';
131+
localStorage.setItem(storageKey, Date.now());
132+
133+
window.dispatchEvent(new StorageEvent('storage', {
134+
key: storageKey,
135+
newValue: Date.now().toString(),
136+
}));
137+
}
128138
returnToUnit(response.data);
129139
},
130140
}));

src/editors/data/redux/thunkActions/app.test.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,11 @@ describe('app thunkActions', () => {
352352
});
353353
it('dispatches actions.app.setSaveResponse with response and then calls returnToUnit', () => {
354354
dispatch.mockClear();
355-
const response = 'testRESPONSE';
355+
const mockParsedData = { has_changes: true };
356+
const response = {
357+
config: { data: JSON.stringify(mockParsedData) },
358+
data: {},
359+
};
356360
calls[1][0].saveBlock.onSuccess(response);
357361
expect(dispatch).toHaveBeenCalledWith(actions.app.setSaveResponse(response));
358362
expect(returnToUnit).toHaveBeenCalled();

0 commit comments

Comments
 (0)