Skip to content

Commit

Permalink
fix: [AXIMST-481] Unit page - fix configuration modal valiation (#178)
Browse files Browse the repository at this point in the history
  • Loading branch information
ihor-romaniuk authored and monteri committed Apr 1, 2024
1 parent bd97a9b commit 7c9427d
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/course-unit/course-xblock/CourseXBlock.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const CourseXBlock = ({
category: COURSE_BLOCK_NAMES.component.id,
displayName: title,
userPartitionInfo,
showCorrectness: 'never',
showCorrectness: 'always',
};

const onDeleteSubmit = () => {
Expand Down
7 changes: 4 additions & 3 deletions src/course-unit/course-xblock/CourseXBlock.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,7 @@ describe('<CourseXBlock />', () => {
expect(handleConfigureSubmitMock).not.toHaveBeenCalled();
});

// ToDo: fix text due to changes generic/configure-modal/ConfigureModal.jsx
it.skip('handles submit restrict access data when save button is clicked', async () => {
it('handles submit restrict access data when save button is clicked', async () => {
axiosMock
.onPost(getXBlockBaseApiUrl(id), {
publish: PUBLISH_TYPES.republish,
Expand Down Expand Up @@ -243,7 +242,9 @@ describe('<CourseXBlock />', () => {
});
expect(saveModalBtnText).toBeInTheDocument();
userEvent.click(saveModalBtnText);
expect(handleConfigureSubmitMock).toHaveBeenCalledTimes(1);
await waitFor(() => {
expect(handleConfigureSubmitMock).toHaveBeenCalledTimes(1);
});
});
});

Expand Down
10 changes: 8 additions & 2 deletions src/generic/configure-modal/ConfigureModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import React from 'react';
import * as Yup from 'yup';
import PropTypes from 'prop-types';
import { isEqual } from 'lodash';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
ModalDialog,
Expand All @@ -15,6 +16,7 @@ import { Formik } from 'formik';

import { VisibilityTypes } from '../../data/constants';
import { COURSE_BLOCK_NAMES } from '../../constants';
import { deepSortObject } from '../../utils';
import messages from './messages';
import BasicTab from './BasicTab';
import VisibilityTab from './VisibilityTab';
Expand Down Expand Up @@ -275,7 +277,7 @@ const ConfigureModal = ({
validateOnChange
>
{({
values, handleSubmit, dirty, isValid, setFieldValue,
values, handleSubmit, isValid, setFieldValue,
}) => (
<>
<ModalDialog.Body className="configure-modal__body">
Expand All @@ -288,7 +290,11 @@ const ConfigureModal = ({
<ModalDialog.CloseButton variant="tertiary">
{intl.formatMessage(messages.cancelButton)}
</ModalDialog.CloseButton>
<Button data-testid="configure-save-button" onClick={handleSubmit} disabled={!(dirty && isValid)}>
<Button
data-testid="configure-save-button"
onClick={handleSubmit}
disabled={!(!isEqual(deepSortObject(initialValues), deepSortObject(values)) && isValid)}
>
{intl.formatMessage(messages.saveButton)}
</Button>
</ActionRow>
Expand Down
23 changes: 23 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useContext, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useMediaQuery } from 'react-responsive';
import * as Yup from 'yup';
import _ from 'lodash';
import { snakeCase } from 'lodash/string';
import moment from 'moment';
import { getConfig, getPath } from '@edx/frontend-platform';
Expand Down Expand Up @@ -298,3 +299,25 @@ export const objectToQueryString = (obj) => (
(key) => `${key }=${ obj[key]}`,
).join('&')
);

/**
* Recursively deep sorts the properties of an object.
* @param {Object|Array|*} obj - The object to deep sort.
* @returns {Object|Array|*} - The deep sorted object.
*/
export function deepSortObject(obj) {
if (!_.isObject(obj)) {
return obj;
}

if (Array.isArray(obj)) {
return _.map(obj, deepSortObject).sort();
}

return _.chain(obj)
.toPairs() // Convert object to key-value pairs array
.sortBy(0) // Sort by keys
.fromPairs() // Convert back to object
.mapValues(deepSortObject) // Recursively sort property values
.value();
}
70 changes: 69 additions & 1 deletion src/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getConfig, getPath } from '@edx/frontend-platform';

import { getFileSizeToClosestByte, createCorrectInternalRoute } from './utils';
import { getFileSizeToClosestByte, createCorrectInternalRoute, deepSortObject } from './utils';

jest.mock('@edx/frontend-platform', () => ({
getConfig: jest.fn(),
Expand Down Expand Up @@ -77,4 +77,72 @@ describe('FilesAndUploads utils', () => {
expect(result).toBe('/course-authoring/some/path');
});
});
describe('deepSortObject', () => {
it('should deep sort an object with nested properties', () => {
const unsortedObject = {
z: 1,
b: {
c: 3,
a: 2,
},
d: [4, 1, 3, 2],
e: 'hello',
};

const sortedObject = deepSortObject(unsortedObject);

const expectedSortedObject = {
b: {
a: 2,
c: 3,
},
d: [1, 2, 3, 4],
e: 'hello',
z: 1,
};

expect(sortedObject).toEqual(expectedSortedObject);
});
it('should handle arrays and other types correctly', () => {
const unsortedObject = {
z: 1,
b: {
c: 3,
a: 2,
},
d: [4, 1, 3, 2],
e: 'hello',
f: true,
};

const sortedObject = deepSortObject(unsortedObject);

const expectedSortedObject = {
b: {
a: 2,
c: 3,
},
d: [1, 2, 3, 4],
e: 'hello',
f: true,
z: 1,
};

expect(sortedObject).toEqual(expectedSortedObject);
});
it('should not modify the original object', () => {
const unsortedObject = {
z: 1,
b: {
c: 3,
a: 2,
},
d: [4, 1, 3, 2],
e: 'hello',
};
const sortedObject = deepSortObject(unsortedObject);

expect(sortedObject).not.toBe(unsortedObject);
});
});
});

0 comments on commit 7c9427d

Please sign in to comment.