feat: Add drag-n-drop support to course unit, refactor tests.
chore: address review feedback
This commit is contained in:
@@ -94,6 +94,7 @@ const CourseOutline = ({ courseId }) => {
|
||||
handleSectionDragAndDrop,
|
||||
handleSubsectionDragAndDrop,
|
||||
handleVideoSharingOptionChange,
|
||||
handleUnitDragAndDrop,
|
||||
} = useCourseOutline({ courseId });
|
||||
|
||||
const [sections, setSections] = useState(sectionsList);
|
||||
@@ -126,6 +127,27 @@ const CourseOutline = ({ courseId }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const setUnit = (sectionIndex, subsectionIndex) => (updatedUnits) => {
|
||||
const section = { ...sections[sectionIndex] };
|
||||
section.childInfo = { ...section.childInfo };
|
||||
|
||||
const subsection = { ...section.childInfo.children[subsectionIndex] };
|
||||
subsection.childInfo = { ...subsection.childInfo };
|
||||
subsection.childInfo.children = updatedUnits();
|
||||
|
||||
const updatedSubsections = [...section.childInfo.children];
|
||||
updatedSubsections[subsectionIndex] = subsection;
|
||||
section.childInfo.children = updatedSubsections;
|
||||
setSections([...sections.slice(0, sectionIndex), section, ...sections.slice(sectionIndex + 1)]);
|
||||
};
|
||||
|
||||
const finalizeUnitOrder = (section, subsection) => () => (newUnits) => {
|
||||
initialSections = [...sectionsList];
|
||||
handleUnitDragAndDrop(section.id, subsection.id, newUnits.map(unit => unit.id), () => {
|
||||
setSections(() => initialSections);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setSections(sectionsList);
|
||||
}, [sectionsList]);
|
||||
@@ -201,7 +223,7 @@ const CourseOutline = ({ courseId }) => {
|
||||
{sections.length ? (
|
||||
<>
|
||||
<DraggableList itemList={sections} setState={setSections} updateOrder={finalizeSectionOrder}>
|
||||
{sections.map((section, index) => (
|
||||
{sections.map((section, sectionIndex) => (
|
||||
<SectionCard
|
||||
id={section.id}
|
||||
key={section.id}
|
||||
@@ -218,10 +240,10 @@ const CourseOutline = ({ courseId }) => {
|
||||
>
|
||||
<DraggableList
|
||||
itemList={section.childInfo.children}
|
||||
setState={setSubsection(index)}
|
||||
setState={setSubsection(sectionIndex)}
|
||||
updateOrder={finalizeSubsectionOrder(section)}
|
||||
>
|
||||
{section.childInfo.children.map((subsection) => (
|
||||
{section.childInfo.children.map((subsection, subsectionIndex) => (
|
||||
<SubsectionCard
|
||||
key={subsection.id}
|
||||
section={section}
|
||||
@@ -233,20 +255,26 @@ const CourseOutline = ({ courseId }) => {
|
||||
onDuplicateSubmit={handleDuplicateSubsectionSubmit}
|
||||
onNewUnitSubmit={handleNewUnitSubmit}
|
||||
>
|
||||
{subsection.childInfo.children.map((unit) => (
|
||||
<UnitCard
|
||||
key={unit.id}
|
||||
unit={unit}
|
||||
subsection={subsection}
|
||||
section={section}
|
||||
savingStatus={savingStatus}
|
||||
onOpenPublishModal={openPublishModal}
|
||||
onOpenDeleteModal={openDeleteModal}
|
||||
onEditSubmit={handleEditSubmit}
|
||||
onDuplicateSubmit={handleDuplicateUnitSubmit}
|
||||
getTitleLink={getUnitUrl}
|
||||
/>
|
||||
))}
|
||||
<DraggableList
|
||||
itemList={subsection.childInfo.children}
|
||||
setState={setUnit(sectionIndex, subsectionIndex)}
|
||||
updateOrder={finalizeUnitOrder(section, subsection)}
|
||||
>
|
||||
{subsection.childInfo.children.map((unit) => (
|
||||
<UnitCard
|
||||
key={unit.id}
|
||||
unit={unit}
|
||||
subsection={subsection}
|
||||
section={section}
|
||||
savingStatus={savingStatus}
|
||||
onOpenPublishModal={openPublishModal}
|
||||
onOpenDeleteModal={openDeleteModal}
|
||||
onEditSubmit={handleEditSubmit}
|
||||
onDuplicateSubmit={handleDuplicateUnitSubmit}
|
||||
getTitleLink={getUnitUrl}
|
||||
/>
|
||||
))}
|
||||
</DraggableList>
|
||||
</SubsectionCard>
|
||||
))}
|
||||
</DraggableList>
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
getCourseBlockApiUrl,
|
||||
getCourseItemApiUrl,
|
||||
getXBlockBaseApiUrl,
|
||||
getChapterBlockApiUrl,
|
||||
} from './data/api';
|
||||
import { RequestStatus } from '../data/constants';
|
||||
import {
|
||||
@@ -688,108 +687,157 @@ describe('<CourseOutline />', () => {
|
||||
});
|
||||
|
||||
it('check that new section list is saved when dragged', async () => {
|
||||
const { getAllByRole } = render(<RootWrapper />);
|
||||
|
||||
const { findAllByRole } = render(<RootWrapper />);
|
||||
const courseBlockId = courseOutlineIndexMock.courseStructure.id;
|
||||
const sectionsDraggers = await findAllByRole('button', { name: 'Drag to reorder' });
|
||||
const draggableButton = sectionsDraggers[7];
|
||||
|
||||
axiosMock
|
||||
.onPut(getCourseBlockApiUrl(courseBlockId))
|
||||
.reply(200, { dummy: 'value' });
|
||||
|
||||
const section1 = store.getState().courseOutline.sectionsList[0].id;
|
||||
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await waitFor(async () => {
|
||||
const sectionsDraggers = await getAllByRole('button', { name: 'Drag to reorder' });
|
||||
|
||||
axiosMock
|
||||
.onPut(getCourseBlockApiUrl(courseBlockId))
|
||||
.reply(200, { dummy: 'value' });
|
||||
|
||||
const section1 = store.getState().courseOutline.sectionsList[0].id;
|
||||
const draggableButton = sectionsDraggers[7];
|
||||
fireEvent.keyDown(draggableButton, { code: 'Space' });
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await act(async () => fireEvent.keyDown(draggableButton, { code: 'Space' }));
|
||||
|
||||
const saveStatus = store.getState().courseOutline.savingStatus;
|
||||
expect(saveStatus).toEqual(RequestStatus.SUCCESSFUL);
|
||||
|
||||
const section2 = store.getState().courseOutline.sectionsList[1].id;
|
||||
expect(section1).toBe(section2);
|
||||
});
|
||||
|
||||
const section2 = store.getState().courseOutline.sectionsList[1].id;
|
||||
expect(section1).toBe(section2);
|
||||
});
|
||||
|
||||
it('check section list is restored to original order when API call fails', async () => {
|
||||
const { getAllByRole } = render(<RootWrapper />);
|
||||
|
||||
const { findAllByRole } = render(<RootWrapper />);
|
||||
const courseBlockId = courseOutlineIndexMock.courseStructure.id;
|
||||
const sectionsDraggers = await findAllByRole('button', { name: 'Drag to reorder' });
|
||||
const draggableButton = sectionsDraggers[6];
|
||||
|
||||
axiosMock
|
||||
.onPut(getCourseBlockApiUrl(courseBlockId))
|
||||
.reply(500);
|
||||
|
||||
const section1 = store.getState().courseOutline.sectionsList[0].id;
|
||||
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await waitFor(async () => {
|
||||
const sectionsDraggers = await getAllByRole('button', { name: 'Drag to reorder' });
|
||||
|
||||
axiosMock
|
||||
.onPut(getCourseBlockApiUrl(courseBlockId))
|
||||
.reply(500);
|
||||
|
||||
const section1 = store.getState().courseOutline.sectionsList[0].id;
|
||||
const draggableButton = sectionsDraggers[6];
|
||||
fireEvent.keyDown(draggableButton, { code: 'Space' });
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await act(async () => fireEvent.keyDown(draggableButton, { code: 'Space' }));
|
||||
|
||||
const saveStatus = store.getState().courseOutline.savingStatus;
|
||||
expect(saveStatus).toEqual(RequestStatus.FAILED);
|
||||
|
||||
const section1New = store.getState().courseOutline.sectionsList[0].id;
|
||||
expect(section1).toBe(section1New);
|
||||
});
|
||||
|
||||
const section1New = store.getState().courseOutline.sectionsList[0].id;
|
||||
expect(section1).toBe(section1New);
|
||||
});
|
||||
|
||||
it('check that new subsection list is saved when dragged', async () => {
|
||||
const { findAllByTestId } = render(<RootWrapper />);
|
||||
|
||||
const courseBlockId = courseOutlineIndexMock.courseStructure.id;
|
||||
const [sectionElement] = await findAllByTestId('section-card');
|
||||
const [section] = store.getState().courseOutline.sectionsList;
|
||||
const subsectionsDraggers = within(sectionElement).getAllByRole('button', { name: 'Drag to reorder' });
|
||||
const draggableButton = subsectionsDraggers[1];
|
||||
|
||||
axiosMock
|
||||
.onPut(getCourseItemApiUrl(section.id))
|
||||
.reply(200, { dummy: 'value' });
|
||||
|
||||
const subsection1 = section.childInfo.children[0].id;
|
||||
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await waitFor(async () => {
|
||||
const [section] = await findAllByTestId('section-card');
|
||||
const subsectionsDraggers = within(section).getAllByRole('button', { name: 'Drag to reorder' });
|
||||
|
||||
axiosMock
|
||||
.onPut(getChapterBlockApiUrl(courseBlockId, store.getState().courseOutline.sectionsList[0].id))
|
||||
.reply(200, { dummy: 'value' });
|
||||
|
||||
const subsection1 = store.getState().courseOutline.sectionsList[0].childInfo.children[0].id;
|
||||
|
||||
// Move the second subsection up
|
||||
const draggableButton = subsectionsDraggers[1];
|
||||
fireEvent.keyDown(draggableButton, { code: 'Space' });
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await act(async () => fireEvent.keyDown(draggableButton, { code: 'Space' }));
|
||||
|
||||
const saveStatus = store.getState().courseOutline.savingStatus;
|
||||
expect(saveStatus).toEqual(RequestStatus.SUCCESSFUL);
|
||||
|
||||
const subsection2 = store.getState().courseOutline.sectionsList[0].childInfo.children[1].id;
|
||||
expect(subsection1).toBe(subsection2);
|
||||
});
|
||||
|
||||
const subsection2 = store.getState().courseOutline.sectionsList[0].childInfo.children[1].id;
|
||||
expect(subsection1).toBe(subsection2);
|
||||
});
|
||||
|
||||
it('check that new subsection list is restored to original order when API call fails', async () => {
|
||||
const { findAllByTestId } = render(<RootWrapper />);
|
||||
|
||||
const courseBlockId = courseOutlineIndexMock.courseStructure.id;
|
||||
const [sectionElement] = await findAllByTestId('section-card');
|
||||
const [section] = store.getState().courseOutline.sectionsList;
|
||||
const subsectionsDraggers = within(sectionElement).getAllByRole('button', { name: 'Drag to reorder' });
|
||||
const draggableButton = subsectionsDraggers[1];
|
||||
|
||||
axiosMock
|
||||
.onPut(getCourseItemApiUrl(section.id))
|
||||
.reply(500);
|
||||
|
||||
const subsection1 = section.childInfo.children[0].id;
|
||||
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await waitFor(async () => {
|
||||
const [section] = await findAllByTestId('section-card');
|
||||
const subsectionsDraggers = within(section).getAllByRole('button', { name: 'Drag to reorder' });
|
||||
|
||||
axiosMock
|
||||
.onPut(getChapterBlockApiUrl(courseBlockId, store.getState().courseOutline.sectionsList[0].id))
|
||||
.reply(500);
|
||||
|
||||
const subsection1 = store.getState().courseOutline.sectionsList[0].childInfo.children[0].id;
|
||||
|
||||
// Move the second subsection up
|
||||
const draggableButton = subsectionsDraggers[1];
|
||||
fireEvent.keyDown(draggableButton, { code: 'Space' });
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await act(async () => fireEvent.keyDown(draggableButton, { code: 'Space' }));
|
||||
|
||||
const saveStatus = store.getState().courseOutline.savingStatus;
|
||||
expect(saveStatus).toEqual(RequestStatus.FAILED);
|
||||
|
||||
const subsection1New = store.getState().courseOutline.sectionsList[0].childInfo.children[0].id;
|
||||
expect(subsection1).toBe(subsection1New);
|
||||
});
|
||||
|
||||
const subsection1New = store.getState().courseOutline.sectionsList[0].childInfo.children[0].id;
|
||||
expect(subsection1).toBe(subsection1New);
|
||||
});
|
||||
|
||||
it('check that new unit list is saved when dragged', async () => {
|
||||
const { findAllByTestId } = render(<RootWrapper />);
|
||||
const subsectionElement = (await findAllByTestId('subsection-card'))[3];
|
||||
const [subsection] = store.getState().courseOutline.sectionsList[1].childInfo.children;
|
||||
const expandBtn = within(subsectionElement).getByTestId('subsection-card-header__expanded-btn');
|
||||
fireEvent.click(expandBtn);
|
||||
const unitDraggers = await within(subsectionElement).findAllByRole('button', { name: 'Drag to reorder' });
|
||||
const draggableButton = unitDraggers[1];
|
||||
|
||||
axiosMock
|
||||
.onPut(getCourseItemApiUrl(subsection.id))
|
||||
.reply(200, { dummy: 'value' });
|
||||
|
||||
const unit1 = subsection.childInfo.children[0].id;
|
||||
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await waitFor(async () => {
|
||||
fireEvent.keyDown(draggableButton, { code: 'Space' });
|
||||
|
||||
const saveStatus = store.getState().courseOutline.savingStatus;
|
||||
expect(saveStatus).toEqual(RequestStatus.SUCCESSFUL);
|
||||
});
|
||||
|
||||
const unit2 = store.getState().courseOutline.sectionsList[1].childInfo.children[0].childInfo.children[1].id;
|
||||
expect(unit1).toBe(unit2);
|
||||
});
|
||||
|
||||
it('check that new unit list is restored to original order when API call fails', async () => {
|
||||
const { findAllByTestId } = render(<RootWrapper />);
|
||||
const subsectionElement = (await findAllByTestId('subsection-card'))[3];
|
||||
const [subsection] = store.getState().courseOutline.sectionsList[1].childInfo.children;
|
||||
const expandBtn = within(subsectionElement).getByTestId('subsection-card-header__expanded-btn');
|
||||
fireEvent.click(expandBtn);
|
||||
const unitDraggers = await within(subsectionElement).findAllByRole('button', { name: 'Drag to reorder' });
|
||||
const draggableButton = unitDraggers[1];
|
||||
|
||||
axiosMock
|
||||
.onPut(getCourseItemApiUrl(subsection.id))
|
||||
.reply(500);
|
||||
|
||||
const unit1 = subsection.childInfo.children[0].id;
|
||||
|
||||
fireEvent.keyDown(draggableButton, { key: 'ArrowUp' });
|
||||
await waitFor(async () => {
|
||||
fireEvent.keyDown(draggableButton, { code: 'Space' });
|
||||
|
||||
const saveStatus = store.getState().courseOutline.savingStatus;
|
||||
expect(saveStatus).toEqual(RequestStatus.FAILED);
|
||||
});
|
||||
|
||||
const unit1New = store.getState().courseOutline.sectionsList[1].childInfo.children[0].childInfo.children[0].id;
|
||||
expect(unit1).toBe(unit1New);
|
||||
});
|
||||
|
||||
it('check that drag handle is not visible for non-draggable sections', async () => {
|
||||
|
||||
@@ -24,12 +24,6 @@ export const getCourseBlockApiUrl = (courseId) => {
|
||||
return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@course+block@course`;
|
||||
};
|
||||
|
||||
export const getChapterBlockApiUrl = (courseId, chapterId) => {
|
||||
const formattedCourseId = courseId.split('course-v1:')[1];
|
||||
const formattedChapterId = chapterId.split('@').slice(-1)[0];
|
||||
return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@chapter+block@${formattedChapterId}`;
|
||||
};
|
||||
|
||||
export const getCourseReindexApiUrl = (reindexLink) => `${getApiBaseUrl()}${reindexLink}`;
|
||||
export const getXBlockBaseApiUrl = () => `${getApiBaseUrl()}/xblock/`;
|
||||
export const getCourseItemApiUrl = (itemId) => `${getXBlockBaseApiUrl()}${itemId}`;
|
||||
@@ -320,14 +314,13 @@ export async function setSectionOrderList(courseId, children) {
|
||||
|
||||
/**
|
||||
* Set order for the list of the subsections
|
||||
* @param {string} courseId
|
||||
* @param {string} sectionId
|
||||
* @param {string} itemId Subsection or unit ID
|
||||
* @param {Array<string>} children list of sections id's
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export async function setSubsectionOrderList(courseId, sectionId, children) {
|
||||
export async function setCourseItemOrderList(itemId, children) {
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.put(getChapterBlockApiUrl(courseId, sectionId), {
|
||||
.put(getCourseItemApiUrl(itemId), {
|
||||
children,
|
||||
});
|
||||
|
||||
|
||||
@@ -104,6 +104,15 @@ const slice = createSlice({
|
||||
sections[i].childInfo.children.sort((a, b) => subsectionListIds.indexOf(a.id) - subsectionListIds.indexOf(b.id));
|
||||
state.sectionsList = [...sections];
|
||||
},
|
||||
reorderUnitList: (state, { payload }) => {
|
||||
const { sectionId, subsectionId, unitListIds } = payload;
|
||||
const sections = [...state.sectionsList];
|
||||
const i = sections.findIndex(section => section.id === sectionId);
|
||||
const j = sections[i].childInfo.children.findIndex(subsection => subsection.id === subsectionId);
|
||||
const subsection = sections[i].childInfo.children[j];
|
||||
subsection.childInfo.children.sort((a, b) => unitListIds.indexOf(a.id) - unitListIds.indexOf(b.id));
|
||||
state.sectionsList = [...sections];
|
||||
},
|
||||
setCurrentSection: (state, { payload }) => {
|
||||
state.currentSection = payload;
|
||||
},
|
||||
@@ -193,6 +202,7 @@ export const {
|
||||
duplicateSection,
|
||||
reorderSectionList,
|
||||
reorderSubsectionList,
|
||||
reorderUnitList,
|
||||
} = slice.actions;
|
||||
|
||||
export const {
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
updateCourseSectionHighlights,
|
||||
setSectionOrderList,
|
||||
setVideoSharingOption,
|
||||
setSubsectionOrderList,
|
||||
setCourseItemOrderList,
|
||||
} from './api';
|
||||
import {
|
||||
addSection,
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
duplicateSection,
|
||||
reorderSectionList,
|
||||
reorderSubsectionList,
|
||||
reorderUnitList,
|
||||
} from './slice';
|
||||
|
||||
export function fetchCourseOutlineIndexQuery(courseId) {
|
||||
@@ -468,13 +469,13 @@ export function setSectionOrderListQuery(courseId, sectionListIds, restoreCallba
|
||||
};
|
||||
}
|
||||
|
||||
export function setSubsectionOrderListQuery(courseId, sectionId, subsectionListIds, restoreCallback) {
|
||||
export function setSubsectionOrderListQuery(sectionId, subsectionListIds, restoreCallback) {
|
||||
return async (dispatch) => {
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
|
||||
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving));
|
||||
|
||||
try {
|
||||
await setSubsectionOrderList(courseId, sectionId, subsectionListIds).then(async (result) => {
|
||||
await setCourseItemOrderList(sectionId, subsectionListIds).then(async (result) => {
|
||||
if (result) {
|
||||
dispatch(reorderSubsectionList({ sectionId, subsectionListIds }));
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
@@ -488,3 +489,24 @@ export function setSubsectionOrderListQuery(courseId, sectionId, subsectionListI
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function setUnitOrderListQuery(sectionId, subsectionId, unitListIds, restoreCallback) {
|
||||
return async (dispatch) => {
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
|
||||
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving));
|
||||
|
||||
try {
|
||||
await setCourseItemOrderList(subsectionId, unitListIds).then(async (result) => {
|
||||
if (result) {
|
||||
dispatch(reorderUnitList({ sectionId, subsectionId, unitListIds }));
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
dispatch(hideProcessingNotification());
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
restoreCallback();
|
||||
dispatch(hideProcessingNotification());
|
||||
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
setSectionOrderListQuery,
|
||||
setVideoSharingOptionQuery,
|
||||
setSubsectionOrderListQuery,
|
||||
setUnitOrderListQuery,
|
||||
} from './data/thunk';
|
||||
|
||||
const useCourseOutline = ({ courseId }) => {
|
||||
@@ -191,13 +192,17 @@ const useCourseOutline = ({ courseId }) => {
|
||||
};
|
||||
|
||||
const handleSubsectionDragAndDrop = (sectionId, subsectionListIds, restoreCallback) => {
|
||||
dispatch(setSubsectionOrderListQuery(courseId, sectionId, subsectionListIds, restoreCallback));
|
||||
dispatch(setSubsectionOrderListQuery(sectionId, subsectionListIds, restoreCallback));
|
||||
};
|
||||
|
||||
const handleVideoSharingOptionChange = (value) => {
|
||||
dispatch(setVideoSharingOptionQuery(courseId, value));
|
||||
};
|
||||
|
||||
const handleUnitDragAndDrop = (sectionId, subsectionId, unitListIds, restoreCallback) => {
|
||||
dispatch(setUnitOrderListQuery(sectionId, subsectionId, unitListIds, restoreCallback));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchCourseOutlineIndexQuery(courseId));
|
||||
dispatch(fetchCourseBestPracticesQuery({ courseId }));
|
||||
@@ -261,6 +266,7 @@ const useCourseOutline = ({ courseId }) => {
|
||||
handleSectionDragAndDrop,
|
||||
handleSubsectionDragAndDrop,
|
||||
handleVideoSharingOptionChange,
|
||||
handleUnitDragAndDrop,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -96,7 +96,9 @@ const UnitCard = ({
|
||||
<ConditionalSortableElement
|
||||
id={id}
|
||||
key={id}
|
||||
draggable={false} // update to {actions.draggable} when unit drag-n-drop is implemented
|
||||
draggable={
|
||||
actions.draggable && !(isHeaderVisible === false)
|
||||
}
|
||||
componentStyle={{
|
||||
background: '#fdfdfd',
|
||||
...borderStyle,
|
||||
|
||||
Reference in New Issue
Block a user