Skip to content

Commit

Permalink
refactor: [AXIMST-658] Course unit - Copy/paste refactoring (#204)
Browse files Browse the repository at this point in the history
* refactor: copy paste functional refactoring

* refactor: refactoring paste-button

* refactor: tests refactoring

* refactor: updated translations

* refactor: refactoring after review

* refactor: renamed status selector name
  • Loading branch information
PKulkoRaccoonGang authored and ihor-romaniuk committed Apr 23, 2024
1 parent d4fe410 commit 2a89806
Show file tree
Hide file tree
Showing 51 changed files with 264 additions and 531 deletions.
73 changes: 0 additions & 73 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
"@openedx/paragon": "^22.2.1",
"@reduxjs/toolkit": "1.9.7",
"@tanstack/react-query": "4.36.1",
"broadcast-channel": "^7.0.0",
"classnames": "2.2.6",
"core-js": "3.8.1",
"email-validator": "2.0.4",
Expand Down
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions src/__mocks__/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as clipboardUnit } from './clipboardUnit';
export { default as clipboardXBlock } from './clipboardXBlock';
11 changes: 11 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,14 @@ export const COURSE_BLOCK_NAMES = ({
vertical: { id: 'vertical', name: 'Unit' },
component: { id: 'component', name: 'Component' },
});

export const STUDIO_CLIPBOARD_CHANNEL = 'studio_clipboard_channel';

export const CLIPBOARD_STATUS = {
loading: 'loading',
ready: 'ready',
expired: 'expired',
error: 'error',
};

export const NOT_XBLOCK_TYPES = ['vertical', 'sequential', 'chapter', 'course'];
1 change: 0 additions & 1 deletion src/course-outline/CourseOutline.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
@import "./publish-modal/PublishModal";
@import "./drag-helper/SortableItem";
@import "./xblock-status/XBlockStatus";
@import "./paste-button/PasteButton";

div.row:has(> div > div.highlight) {
animation: 5s glow;
Expand Down
48 changes: 20 additions & 28 deletions src/course-outline/CourseOutline.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
} from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp } from '@edx/frontend-platform';
import { getConfig, initializeMockApp } from '@edx/frontend-platform';
import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
Expand All @@ -19,7 +19,6 @@ import {
getCourseBlockApiUrl,
getCourseItemApiUrl,
getXBlockBaseApiUrl,
getClipboardUrl,
} from './data/api';
import { RequestStatus } from '../data/constants';
import {
Expand All @@ -37,16 +36,19 @@ import {
courseSectionMock,
courseSubsectionMock,
} from './__mocks__';
import { clipboardUnit } from '../__mocks__';
import { executeThunk } from '../utils';
import { COURSE_BLOCK_NAMES, VIDEO_SHARING_OPTIONS } from './constants';
import CourseOutline from './CourseOutline';

import configureModalMessages from '../generic/configure-modal/messages';
import pasteButtonMessages from '../generic/clipboard/paste-component/messages';
import messages from './messages';
import { getClipboardUrl } from '../generic/data/api';
import headerMessages from './header-navigations/messages';
import cardHeaderMessages from './card-header/messages';
import enableHighlightsModalMessages from './enable-highlights-modal/messages';
import statusBarMessages from './status-bar/messages';
import pasteButtonMessages from './paste-button/messages';
import subsectionMessages from './subsection-card/messages';
import pageAlertMessages from './page-alerts/messages';
import {
Expand All @@ -55,7 +57,6 @@ import {
moveSubsection,
moveUnit,
} from './drag-helper/utils';
import messages from './messages';

let axiosMock;
let store;
Expand All @@ -64,6 +65,13 @@ const courseId = '123';

window.HTMLElement.prototype.scrollIntoView = jest.fn();

const clipboardBroadcastChannelMock = {
postMessage: jest.fn(),
close: jest.fn(),
};

global.BroadcastChannel = jest.fn(() => clipboardBroadcastChannelMock);

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
Expand Down Expand Up @@ -2080,7 +2088,7 @@ describe('<CourseOutline />', () => {
});

it('check whether unit copy & paste option works correctly', async () => {
const { findAllByTestId, findAllByRole } = render(<RootWrapper />);
const { findAllByTestId, queryByTestId, findAllByRole } = render(<RootWrapper />);
// get first section -> first subsection -> first unit element
const [section] = courseOutlineIndexMock.courseStructure.childInfo.children;
const [sectionElement] = await findAllByTestId('section-card');
Expand All @@ -2091,27 +2099,11 @@ describe('<CourseOutline />', () => {
const [unit] = subsection.childInfo.children;
const [unitElement] = await within(subsectionElement).findAllByTestId('unit-card');

const expectedClipboardContent = {
content: {
blockType: 'vertical',
blockTypeDisplay: 'Unit',
created: '2024-01-29T07:58:36.844249Z',
displayName: unit.displayName,
id: 15,
olxUrl: 'http://localhost:18010/api/content-staging/v1/staged-content/15/olx',
purpose: 'clipboard',
status: 'ready',
userId: 3,
},
sourceUsageKey: unit.id,
sourceContexttitle: courseOutlineIndexMock.courseStructure.displayName,
sourceEditUrl: unit.studioUrl,
};
// mock api call
axiosMock
.onPost(getClipboardUrl(), {
usage_key: unit.id,
}).reply(200, expectedClipboardContent);
}).reply(200, clipboardUnit);
// check that initialUserClipboard state is empty
const { initialUserClipboard } = store.getState().courseOutline;
expect(initialUserClipboard).toBeUndefined();
Expand All @@ -2125,19 +2117,19 @@ describe('<CourseOutline />', () => {
await act(async () => fireEvent.click(copyButton));

// check that initialUserClipboard state is updated
expect(store.getState().courseOutline.initialUserClipboard).toEqual(expectedClipboardContent);
expect(store.getState().generic.clipboardData).toEqual(clipboardUnit);

[subsectionElement] = await within(sectionElement).findAllByTestId('subsection-card');
// find clipboard content label
const clipboardLabel = await within(subsectionElement).findByText(
pasteButtonMessages.clipboardContentLabel.defaultMessage,
pasteButtonMessages.pasteButtonWhatsInClipboardText.defaultMessage,
);
await act(async () => fireEvent.mouseOver(clipboardLabel));

// find clipboard content popup link
expect(
subsectionElement.querySelector('#vertical-paste-button-overlay'),
).toHaveAttribute('href', unit.studioUrl);
// find clipboard content popover link
const popoverContent = queryByTestId('popover-content');
expect(popoverContent.tagName).toBe('A');
expect(popoverContent).toHaveAttribute('href', `${getConfig().STUDIO_BASE_URL}${unit.studioUrl}`);

// check paste button functionality
// mock api call
Expand Down
14 changes: 0 additions & 14 deletions src/course-outline/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,20 +434,6 @@ export async function setVideoSharingOption(courseId, videoSharingOption) {
return data;
}

/**
* Copy block to clipboard
* @param {string} usageKey
* @returns {Promise<Object>}
*/
export async function copyBlockToClipboard(usageKey) {
const { data } = await getAuthenticatedHttpClient()
.post(getClipboardUrl(), {
usage_key: usageKey,
});

return camelCaseObject(data);
}

/**
* Paste block to clipboard
* @param {string} parentLocator
Expand Down
1 change: 0 additions & 1 deletion src/course-outline/data/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,5 @@ export const getCurrentSection = (state) => state.courseOutline.currentSection;
export const getCurrentSubsection = (state) => state.courseOutline.currentSubsection;
export const getCourseActions = (state) => state.courseOutline.actions;
export const getCustomRelativeDatesActiveFlag = (state) => state.courseOutline.isCustomRelativeDatesActive;
export const getInitialUserClipboard = (state) => state.courseOutline.initialUserClipboard;
export const getProctoredExamsFlag = (state) => state.courseOutline.enableProctoredExams;
export const getPasteFileNotices = (state) => state.courseOutline.pasteFileNotices;
11 changes: 0 additions & 11 deletions src/course-outline/data/slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ const slice = createSlice({
childAddable: true,
duplicable: true,
},
initialUserClipboard: {
content: {},
sourceUsageKey: null,
sourceContexttitle: null,
sourceEditUrl: null,
},
enableProctoredExams: false,
pasteFileNotices: {},
},
Expand All @@ -52,7 +46,6 @@ const slice = createSlice({
state.outlineIndexData = payload;
state.sectionsList = payload.courseStructure?.childInfo?.children || [];
state.isCustomRelativeDatesActive = payload.isCustomRelativeDatesActive;
state.initialUserClipboard = payload.initialUserClipboard;
state.enableProctoredExams = payload.courseStructure?.enableProctoredExams;
},
updateOutlineIndexLoadingStatus: (state, { payload }) => {
Expand All @@ -79,9 +72,6 @@ const slice = createSlice({
...payload,
};
},
updateClipboardContent: (state, { payload }) => {
state.initialUserClipboard = payload;
},
updateCourseActions: (state, { payload }) => {
state.actions = {
...state.actions,
Expand Down Expand Up @@ -210,7 +200,6 @@ export const {
reorderSectionList,
reorderSubsectionList,
reorderUnitList,
updateClipboardContent,
setPasteFileNotices,
removePasteFileNotices,
} = slice.actions;
Expand Down
Loading

0 comments on commit 2a89806

Please sign in to comment.