Skip to content

Commit

Permalink
feat: [AXIMST-75, AXIMST-69, AXIMST-81] Group Configurations - Conten…
Browse files Browse the repository at this point in the history
…t group actions (#165)

* feat: [AXIMST-75, AXIMST-69, AXIMST-81] Content group actions

* fix: resolve conversations
  • Loading branch information
ruzniaievdm authored and monteri committed Apr 1, 2024
1 parent bd7e022 commit 43fef93
Show file tree
Hide file tree
Showing 38 changed files with 1,057 additions and 155 deletions.
6 changes: 3 additions & 3 deletions src/group-configurations/GroupConfigurations.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

import initializeStore from '../store';
import { executeThunk } from '../utils';
import { getGroupConfigurationsApiUrl } from './data/api';
import { getContentStoreApiUrl } from './data/api';
import { fetchGroupConfigurationsQuery } from './data/thunk';
import { groupConfigurationResponseMock } from './__mocks__';
import messages from './messages';
Expand Down Expand Up @@ -43,7 +43,7 @@ describe('<GroupConfigurations />', () => {
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock
.onGet(getGroupConfigurationsApiUrl(courseId))
.onGet(getContentStoreApiUrl(courseId))
.reply(200, groupConfigurationResponseMock);
await executeThunk(fetchGroupConfigurationsQuery(courseId), store.dispatch);
});
Expand Down Expand Up @@ -80,7 +80,7 @@ describe('<GroupConfigurations />', () => {
shouldShowEnrollmentTrack: false,
};
axiosMock
.onGet(getGroupConfigurationsApiUrl(courseId))
.onGet(getContentStoreApiUrl(courseId))
.reply(200, shouldNotShowEnrollmentTrackResponse);

const { queryByTestId } = renderComponent();
Expand Down
30 changes: 30 additions & 0 deletions src/group-configurations/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import PropTypes from 'prop-types';

const availableGroupPropTypes = {
active: PropTypes.bool,
description: PropTypes.string,
groups: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.number,
name: PropTypes.string,
usage: PropTypes.arrayOf(
PropTypes.shape({
label: PropTypes.string,
url: PropTypes.string,
}),
),
version: PropTypes.number,
}),
),
id: PropTypes.number,
name: PropTypes.string,
parameters: PropTypes.shape({
courseId: PropTypes.string,
}),
readOnly: PropTypes.bool,
scheme: PropTypes.string,
version: PropTypes.number,
};

// eslint-disable-next-line import/prefer-default-export
export { availableGroupPropTypes };
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import PropTypes from 'prop-types';
import { Formik } from 'formik';
import * as Yup from 'yup';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Alert,
ActionRow,
Button,
Form,
OverlayTrigger,
Tooltip,
} from '@openedx/paragon';

import { WarningFilled as WarningFilledIcon } from '@openedx/paragon/icons';

import PromptIfDirty from '../../generic/PromptIfDirty';
import { isAlreadyExistsGroup } from './utils';
import messages from './messages';

const ContentGroupContainer = ({
isEditMode,
groupNames,
isUsedInLocation,
overrideValue,
onCreateClick,
onCancelClick,
onDeleteClick,
onEditClick,
}) => {
const { formatMessage } = useIntl();
const initialValues = { newGroupName: overrideValue };
const validationSchema = Yup.object().shape({
newGroupName: Yup.string()
.required(formatMessage(messages.requiredError))
.test(
'unique-name-restriction',
formatMessage(messages.invalidMessage),
(value) => overrideValue === value || !isAlreadyExistsGroup(groupNames, value),
),
});
const onSubmitForm = isEditMode ? onEditClick : onCreateClick;

return (
<div className="configuration-card" data-testid="content-group-new">
<div className="configuration-card-header">
<h3>{formatMessage(messages.newGroupHeader)}</h3>
</div>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
validateOnChange={false}
validateOnBlur={false}
onSubmit={onSubmitForm}
>
{({
values, errors, dirty, handleChange, handleSubmit,
}) => {
const isInvalid = !!errors.newGroupName;

return (
<>
<Form.Group
className="mt-3 mb-4 content-group-new__input"
isInvalid={isInvalid}
>
<Form.Control
value={values.newGroupName}
name="newGroupName"
onChange={handleChange}
placeholder={formatMessage(messages.newGroupInputPlaceholder)}
/>
{isInvalid && (
<Form.Control.Feedback type="invalid">
{errors.newGroupName}
</Form.Control.Feedback>
)}
</Form.Group>
{isUsedInLocation && (
<Alert
variant="warning"
icon={WarningFilledIcon}
className="my-3"
>
<p>{formatMessage(messages.alertGroupInUsage)}</p>
</Alert>
)}
<ActionRow>
{isEditMode && (
<OverlayTrigger
overlay={(
<Tooltip
id={`delete-restriction-tooltip-${values.newGroupName}`}
>
{formatMessage(
isUsedInLocation
? messages.deleteRestriction
: messages.deleteButton,
)}
</Tooltip>
)}
>
<Button
disabled={isUsedInLocation}
variant="outline-primary"
onClick={onDeleteClick}
>
{formatMessage(messages.deleteButton)}
</Button>
</OverlayTrigger>
)}
<ActionRow.Spacer />
<Button onClick={onCancelClick} variant="tertiary">
{formatMessage(messages.cancelButton)}
</Button>
<Button onClick={handleSubmit}>
{formatMessage(
isEditMode ? messages.saveButton : messages.createButton,
)}
</Button>
</ActionRow>
<PromptIfDirty dirty={dirty} />
</>
);
}}
</Formik>
</div>
);
};

ContentGroupContainer.defaultProps = {
groupNames: [],
overrideValue: '',
isEditMode: false,
isUsedInLocation: false,
onCreateClick: null,
onDeleteClick: null,
onEditClick: null,
};

ContentGroupContainer.propTypes = {
groupNames: PropTypes.arrayOf(PropTypes.string),
isEditMode: PropTypes.bool,
isUsedInLocation: PropTypes.bool,
overrideValue: PropTypes.string,
onCreateClick: PropTypes.func,
onCancelClick: PropTypes.func.isRequired,
onDeleteClick: PropTypes.func,
onEditClick: PropTypes.func,
};

export default ContentGroupContainer;
Loading

0 comments on commit 43fef93

Please sign in to comment.