Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [AXIMST-46] Course unit - Drag And Drop For Xblocks #158

Merged
merged 3 commits into from
Feb 15, 2024

Conversation

PKulkoRaccoonGang
Copy link

@codecov-commenter
Copy link

codecov-commenter commented Feb 13, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

❗ No coverage uploaded for pull request base (ts-develop@d835b8f). Click here to learn what that means.

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@              Coverage Diff              @@
##             ts-develop     #158   +/-   ##
=============================================
  Coverage              ?   89.58%           
=============================================
  Files                 ?      663           
  Lines                 ?    11014           
  Branches              ?     2307           
=============================================
  Hits                  ?     9867           
  Misses                ?     1112           
  Partials              ?       35           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@PKulkoRaccoonGang PKulkoRaccoonGang force-pushed the Peter_Kulko/xbocks-drag-and-drop branch 3 times, most recently from 40495a3 to 075c782 Compare February 14, 2024 15:41
@PKulkoRaccoonGang PKulkoRaccoonGang marked this pull request as ready for review February 14, 2024 15:54
@PKulkoRaccoonGang PKulkoRaccoonGang changed the title feat: [AXIMST-46] Drag And Drop For Xblocks feat: [AXIMST-46] Course unit - Drag And Drop For Xblocks Feb 14, 2024
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { Container, Layout, Stack } from '@edx/paragon';
import { useIntl, injectIntl } from '@edx/frontend-platform/i18n';
import { ErrorAlert } from '@edx/frontend-lib-content-components';
import { DraggableList, ErrorAlert } from '@edx/frontend-lib-content-components';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ErrorAlert component is imported twice. Remove one of the imports to clean up the code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see only one import)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, my bad

setUnitXBlocks(() => initialUnitXBlocks);
});
};

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
handleXBlockDragAndDrop,
} = useCourseUnit({ courseId, blockId });
const initialXBlocksData = useMemo(() => courseVerticalChildren.children ?? [], [courseVerticalChildren.children]);
const [unitXBlocks, setUnitXBlocks] = useState(initialXBlocksData);
useEffect(() => {
document.title = getPageHeadTitle('', unitTitle);
}, [unitTitle]);
useEffect(() => {
setUnitXBlocks(courseVerticalChildren.children);
}, [courseVerticalChildren.children]);
const {
isShow: isShowProcessingNotification,
title: processingNotificationTitle,
} = useSelector(getProcessingNotification);
if (isLoading) {
return <Loading />;
}
if (sequenceStatus === RequestStatus.FAILED) {
return (
<Container size="xl" className="course-unit px-4 mt-4">
<ConnectionErrorAlert />
</Container>
);
}
const finalizeXBlockOrder = (newXBlocks) => {
handleXBlockDragAndDrop(newXBlocks.map(xBlock => xBlock.id), () => {
setUnitXBlocks(initialXBlocksData);
});
};

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try this, should work

Copy link
Author

@PKulkoRaccoonGang PKulkoRaccoonGang Feb 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a self-invoking function (it is required for DraggableList), the rest of your suggestions are fully taken into account

@@ -1288,4 +1289,56 @@ describe('<CourseUnit />', () => {
expect(queryByTestId('has-error-files')).toBeNull();
});
});

describe('Drag and drop', () => {
it('check xblock list is restored to original order when API call fails', async () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it('check xblock list is restored to original order when API call fails', async () => {
it('checks xblock list is restored to original order when API call fails', async () => {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrected

Comment on lines 53 to 84

/**
* Updates the 'id' property of objects in the data structure using the 'blockId' value where present.
* @param {Object} data - The original data structure to be updated.
* @returns {Object} - The updated data structure with updated 'id' values.
*/
export const updateXBlockBlockIdToId = (data) => {
if (!data?.children?.length) {
return data;
}

const updatedData = { ...data };

updatedData.children.forEach((item, index) => {
if (item.blockId) {
updatedData.children[index] = { ...item, id: item.blockId };
}

const { userPartitionInfo } = item;
if (userPartitionInfo?.selectablePartitions) {
updatedData.children[index].userPartitionInfo = {
...userPartitionInfo,
selectablePartitions: userPartitionInfo.selectablePartitions.map(partition => ({
...partition,
groups: partition.groups.map(group => (group.id ? { ...group, id: group.blockId } : group)),
})),
};
}
});

return updatedData;
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/**
* Updates the 'id' property of objects in the data structure using the 'blockId' value where present.
* @param {Object} data - The original data structure to be updated.
* @returns {Object} - The updated data structure with updated 'id' values.
*/
export const updateXBlockBlockIdToId = (data) => {
if (!data?.children?.length) {
return data;
}
const updatedData = { ...data };
updatedData.children.forEach((item, index) => {
if (item.blockId) {
updatedData.children[index] = { ...item, id: item.blockId };
}
const { userPartitionInfo } = item;
if (userPartitionInfo?.selectablePartitions) {
updatedData.children[index].userPartitionInfo = {
...userPartitionInfo,
selectablePartitions: userPartitionInfo.selectablePartitions.map(partition => ({
...partition,
groups: partition.groups.map(group => (group.id ? { ...group, id: group.blockId } : group)),
})),
};
}
});
return updatedData;
};
export const updateXBlockBlockIdToId = (data) => {
// Base case: if data is not an object or is null, return it unmodified
if (typeof data !== 'object' || data === null) {
return data;
}
// Process arrays recursively
if (Array.isArray(data)) {
return data.map(updateXBlockBlockIdToId);
}
// For objects, create a new object to accumulate the results
const updatedData = {};
Object.keys(data).forEach(key => {
const value = data[key];
// If the current property is 'children' or 'selectablePartitions' or 'groups', process it recursively
if (key === 'children' || key === 'selectablePartitions' || key === 'groups') {
updatedData[key] = updateXBlockBlockIdToId(value);
} else if (key === 'blockId') {
// Replace 'blockId' with 'id', maintaining other properties
updatedData['id'] = value;
} else {
// Copy other properties unchanged
updatedData[key] = value;
}
});
// Special handling for objects with both 'id' and 'blockId' to ensure 'blockId' takes precedence
if ('blockId' in data) {
updatedData['id'] = data['blockId'];
}
return updatedData;
};

Check if this is more effecient implementation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added and deleted unnecessary comments
Thanks

Comment on lines 104 to 105
const handleXBlockDragAndDrop = (xBlockListIds, restoreCallback) => {
dispatch(setXBlockOrderListQuery(blockId, xBlockListIds, restoreCallback));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const handleXBlockDragAndDrop = (xBlockListIds, restoreCallback) => {
dispatch(setXBlockOrderListQuery(blockId, xBlockListIds, restoreCallback));
const handleXBlockDragAndDrop = (xblockListIds, restoreCallback) => {
dispatch(setXBlockOrderListQuery(blockId, xblockListIds, restoreCallback));

Copy link

@GlugovGrGlib GlugovGrGlib Feb 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected, thanks guys

@@ -276,3 +278,26 @@ export function copyToClipboard(usageKey) {
}
};
}

export function setXBlockOrderListQuery(blockId, xBlockListIds, restoreCallback) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export function setXBlockOrderListQuery(blockId, xBlockListIds, restoreCallback) {
export function setXBlockOrderListQuery(blockId, xblockListIds, restoreCallback) {

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 106 to 111
reorderXBlockList: (state, { payload }) => {
const xBlockList = [...state.courseVerticalChildren.children];
xBlockList.sort((a, b) => payload.indexOf(a.id) - payload.indexOf(b.id));

state.courseVerticalChildren.children = [...xBlockList];
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
reorderXBlockList: (state, { payload }) => {
const xBlockList = [...state.courseVerticalChildren.children];
xBlockList.sort((a, b) => payload.indexOf(a.id) - payload.indexOf(b.id));
state.courseVerticalChildren.children = [...xBlockList];
},
reorderXBlockList: (state, { payload }) => {
// Create a map for payload IDs to their index for O(1) lookups
const indexMap = new Map(payload.map((id, index) => [id, index]));
// Directly sort the children based on the order defined in payload
// This avoids the need to copy the array beforehand
state.courseVerticalChildren.children.sort((a, b) => {
return (indexMap.get(a.id) || 0) - (indexMap.get(b.id) || 0);
});
},

indexOf inside sort function will be ineffective.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this code from another page, but your suggestion looks great!

@@ -8,5 +8,6 @@ export const getCourseSectionVertical = (state) => state.courseUnit.courseSectio
export const getCourseId = (state) => state.courseDetail.courseId;
export const getSequenceId = (state) => state.courseUnit.sequenceId;
export const getCourseVerticalChildren = (state) => state.courseUnit.courseVerticalChildren;
export const getCourseVerticalChildren2 = (state) => state.courseUnit.courseVerticalChildren.children;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this just for debug, or where this is used?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Thanks

Copy link

@GlugovGrGlib GlugovGrGlib left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok, except comments above

@monteri monteri merged commit df796f0 into ts-develop Feb 15, 2024
3 checks passed
ihor-romaniuk pushed a commit that referenced this pull request Feb 16, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
ihor-romaniuk pushed a commit that referenced this pull request Feb 29, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
ihor-romaniuk pushed a commit that referenced this pull request Feb 29, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
PKulkoRaccoonGang added a commit that referenced this pull request Mar 18, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
PKulkoRaccoonGang added a commit that referenced this pull request Mar 18, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
khudym pushed a commit that referenced this pull request Mar 25, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
monteri pushed a commit that referenced this pull request Apr 1, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
ihor-romaniuk pushed a commit that referenced this pull request Apr 25, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
monteri pushed a commit that referenced this pull request Apr 29, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
PKulkoRaccoonGang added a commit that referenced this pull request Apr 30, 2024
* feat: [AXIMST-46] Drag And Drop For Xblocks

* feat: added tests

* refactor: refactoring after review
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants