Skip to content

Commit c9daf42

Browse files
Ahtesham QuraishAhtesham Quraish
authored andcommitted
feat: settings design for section, subsection and unit
1 parent 97a805d commit c9daf42

File tree

5 files changed

+345
-1
lines changed

5 files changed

+345
-1
lines changed

src/library-authoring/containers/ContainerInfo.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
} from '../common/context/SidebarContext';
2525
import ContainerOrganize from './ContainerOrganize';
2626
import ContainerUsage from './ContainerUsage';
27+
import { SettingsPanel } from './SettingsPanel';
2728
import { useLibraryRoutes } from '../routes';
2829
import { LibraryUnitBlocks } from '../units/LibraryUnitBlocks';
2930
import { LibraryContainerChildren } from '../section-subsections/LibraryContainerChildren';
@@ -225,7 +226,7 @@ const ContainerInfo = () => {
225226
<ContainerUsage />
226227
</Tab.Pane>
227228
<Tab.Pane eventKey={CONTAINER_INFO_TABS.Settings}>
228-
{/* TODO: Container settings component */}
229+
<SettingsPanel containerType={containerType} />
229230
</Tab.Pane>
230231
</Tab.Content>
231232
</Tab.Container>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.text-muted-override{
2+
color: #DEDBDB !important;
3+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import React from 'react';
2+
import { render, screen } from '@testing-library/react';
3+
import { IntlProvider } from '@edx/frontend-platform/i18n';
4+
import { ContainerType } from '@src/generic/key-utils';
5+
import { SettingsPanel } from './SettingsPanel';
6+
import messages from './messages';
7+
8+
const renderWithIntl = (ui: React.ReactNode) => render(
9+
<IntlProvider locale="en" messages={{}}>
10+
{ui}
11+
</IntlProvider>,
12+
);
13+
14+
describe('SettingsPanel', () => {
15+
describe('Section container', () => {
16+
test('renders section default info text', () => {
17+
renderWithIntl(<SettingsPanel containerType={ContainerType.Section} />);
18+
expect(
19+
screen.getByText(messages.settingsSectionDefaultText.defaultMessage),
20+
).toBeInTheDocument();
21+
});
22+
23+
test('does not render grading or results visibility', () => {
24+
renderWithIntl(<SettingsPanel containerType={ContainerType.Section} />);
25+
expect(
26+
screen.queryByText(messages.settingsSectionGradingLabel.defaultMessage),
27+
).not.toBeInTheDocument();
28+
expect(
29+
screen.queryByText(
30+
messages.settingsSectionAssessmentResultsVisibilityLabel.defaultMessage,
31+
),
32+
).not.toBeInTheDocument();
33+
});
34+
35+
test('renders visibility controls', () => {
36+
renderWithIntl(<SettingsPanel containerType={ContainerType.Section} />);
37+
expect(
38+
screen.getByText(messages.settingsSectionVisibilityLabel.defaultMessage),
39+
).toBeInTheDocument();
40+
expect(
41+
screen.getByRole('button', {
42+
name: messages.settingsSectionDefaultVisibilityButton.defaultMessage,
43+
}),
44+
).toBeDisabled();
45+
});
46+
});
47+
48+
describe('Subsection container', () => {
49+
test('renders subsection default info text', () => {
50+
renderWithIntl(<SettingsPanel containerType={ContainerType.Subsection} />);
51+
expect(
52+
screen.getByText(messages.settingsSubSectionDefaultText.defaultMessage),
53+
).toBeInTheDocument();
54+
});
55+
56+
test('renders grading buttons (disabled)', () => {
57+
renderWithIntl(<SettingsPanel containerType={ContainerType.Subsection} />);
58+
expect(
59+
screen.getByRole('button', {
60+
name: messages.settingsSectionUpgradeButton.defaultMessage,
61+
}),
62+
).toBeDisabled();
63+
expect(
64+
screen.getByRole('button', {
65+
name: messages.settingsSectionGradeButton.defaultMessage,
66+
}),
67+
).toBeDisabled();
68+
});
69+
70+
test('renders visibility + hide content checkbox', () => {
71+
renderWithIntl(<SettingsPanel containerType={ContainerType.Subsection} />);
72+
expect(
73+
screen.getByLabelText(
74+
messages.settingsSectionHideContentAfterDueDateLabel.defaultMessage,
75+
),
76+
).toBeDisabled();
77+
});
78+
79+
test('renders results visibility controls', () => {
80+
renderWithIntl(<SettingsPanel containerType={ContainerType.Subsection} />);
81+
expect(
82+
screen.getByRole('button', {
83+
name: messages.settingsSectionShowButton.defaultMessage,
84+
}),
85+
).toBeDisabled();
86+
expect(
87+
screen.getByRole('button', {
88+
name: messages.settingsSectionHideButton.defaultMessage,
89+
}),
90+
).toBeDisabled();
91+
expect(
92+
screen.getByLabelText(
93+
messages.settingsSectionOnlyShowResultsAfterDueDateLabel.defaultMessage,
94+
),
95+
).toBeDisabled();
96+
});
97+
});
98+
99+
describe('Unit container', () => {
100+
test('renders unit default info text', () => {
101+
renderWithIntl(<SettingsPanel containerType={ContainerType.Unit} />);
102+
expect(
103+
screen.getByText(messages.settingsUnitDefaultText.defaultMessage),
104+
).toBeInTheDocument();
105+
});
106+
107+
test('renders discussion settings', () => {
108+
renderWithIntl(<SettingsPanel containerType={ContainerType.Unit} />);
109+
expect(
110+
screen.getByText(messages.settingsSectionDiscussionLabel.defaultMessage),
111+
).toBeInTheDocument();
112+
expect(
113+
screen.getByLabelText(
114+
messages.settingsSectionEnableDiscussionLabel.defaultMessage,
115+
),
116+
).toBeChecked();
117+
expect(
118+
screen.getByText(messages.settingsSectionUnpublishedUnitsLabel.defaultMessage),
119+
).toBeInTheDocument();
120+
});
121+
});
122+
});
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import React, { useState } from 'react';
2+
import { FormattedMessage } from '@edx/frontend-platform/i18n';
3+
import { Button, ButtonGroup, Form } from '@openedx/paragon';
4+
import { ContainerType } from '@src/generic/key-utils';
5+
import messages from './messages';
6+
7+
import './SettingsPanel.scss';
8+
9+
interface SettingsPanelProps {
10+
containerType: string;
11+
}
12+
13+
export const SettingsPanel: React.FC<SettingsPanelProps> = ({ containerType }) => {
14+
const [grading] = useState('ungraded');
15+
const [visibility] = useState('default');
16+
const [resultsVisibility] = useState('show');
17+
18+
const disableAll = true; // 👈 set to false to re-enable
19+
20+
return (
21+
<>
22+
<div className="pb-2 pl-4 pr-4 space-y-4">
23+
<p className="text-muted small mb-4">
24+
{ containerType === ContainerType.Section && (
25+
<FormattedMessage {...messages.settingsSectionDefaultText} />
26+
)}
27+
{ containerType === ContainerType.Subsection && (
28+
<FormattedMessage {...messages.settingsSubSectionDefaultText} />
29+
)}
30+
{ containerType === ContainerType.Unit && (
31+
<FormattedMessage {...messages.settingsUnitDefaultText} />
32+
)}
33+
</p>
34+
</div>
35+
<div className="pb-4 pl-4 pr-4 space-y-4">
36+
{containerType === ContainerType.Subsection && (
37+
<>
38+
<h6 className="text-muted small font-weight-bold mb-3">
39+
<FormattedMessage {...messages.settingsSectionGradingLabel} />
40+
</h6>
41+
<ButtonGroup className="d-flex w-100 mb-4.5">
42+
<Button
43+
className="flex-fill"
44+
variant={grading === 'ungraded' ? 'dark' : 'outline-secondary'}
45+
size="sm"
46+
disabled={disableAll}
47+
>
48+
<FormattedMessage {...messages.settingsSectionUpgradeButton} />
49+
</Button>
50+
<Button
51+
className="flex-fill"
52+
variant={grading === 'graded' ? 'dark' : 'outline-secondary'}
53+
size="sm"
54+
disabled={disableAll}
55+
>
56+
<FormattedMessage {...messages.settingsSectionGradeButton} />
57+
</Button>
58+
</ButtonGroup>
59+
</>
60+
)}
61+
62+
<h6 className="text-muted small font-weight-bold mt-3 mb-3">
63+
<FormattedMessage {...messages.settingsSectionVisibilityLabel} />
64+
</h6>
65+
<ButtonGroup className="d-flex w-100">
66+
<Button
67+
className="flex-fill"
68+
variant={visibility === 'default' ? 'dark' : 'outline-secondary'}
69+
size="sm"
70+
disabled={disableAll}
71+
>
72+
<FormattedMessage {...messages.settingsSectionDefaultVisibilityButton} />
73+
</Button>
74+
<Button
75+
className="flex-fill"
76+
variant={visibility === 'staff' ? 'dark' : 'outline-secondary'}
77+
size="sm"
78+
disabled={disableAll}
79+
>
80+
<FormattedMessage {...messages.settingsSectionStaffOnlyButton} />
81+
</Button>
82+
</ButtonGroup>
83+
{containerType === ContainerType.Subsection && (
84+
<Form.Checkbox className="mt-3 text-muted mb-4.5" disabled>
85+
<FormattedMessage {...messages.settingsSectionHideContentAfterDueDateLabel} />
86+
</Form.Checkbox>
87+
)}
88+
89+
{containerType === ContainerType.Subsection && (
90+
<>
91+
<h6 className="text-muted small font-weight-bold mt-1 mb-3">
92+
<FormattedMessage {...messages.settingsSectionAssessmentResultsVisibilityLabel} />
93+
</h6>
94+
<ButtonGroup className="d-flex w-100">
95+
<Button
96+
className="flex-fill"
97+
variant={resultsVisibility === 'show' ? 'dark' : 'outline-secondary'}
98+
size="sm"
99+
disabled={disableAll}
100+
>
101+
<FormattedMessage {...messages.settingsSectionShowButton} />
102+
</Button>
103+
<Button
104+
className="flex-fill"
105+
variant={resultsVisibility === 'hide' ? 'dark' : 'outline-secondary'}
106+
size="sm"
107+
disabled={disableAll}
108+
>
109+
<FormattedMessage {...messages.settingsSectionHideButton} />
110+
</Button>
111+
</ButtonGroup>
112+
<Form.Checkbox className="mt-3 text-muted mb-4.5" disabled>
113+
<FormattedMessage {...messages.settingsSectionOnlyShowResultsAfterDueDateLabel} />
114+
</Form.Checkbox>
115+
</>
116+
)}
117+
{containerType === ContainerType.Unit && (
118+
<>
119+
<h6 className="text-muted small font-weight-bold mt-3 mb-3">
120+
<FormattedMessage {...messages.settingsSectionDiscussionLabel} />
121+
</h6>
122+
<Form.Checkbox className="mt-3 text-muted" disabled checked>
123+
<FormattedMessage {...messages.settingsSectionEnableDiscussionLabel} />
124+
</Form.Checkbox>
125+
<p className="text-muted small mb-4 text-muted-override">
126+
<FormattedMessage {...messages.settingsSectionUnpublishedUnitsLabel} />
127+
</p>
128+
</>
129+
)}
130+
</div>
131+
</>
132+
);
133+
};

src/library-authoring/containers/messages.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,91 @@ const messages = defineMessages({
216216
defaultMessage: 'Failed to publish changes',
217217
description: 'Popup text seen if publishing a container fails',
218218
},
219+
settingsSectionDefaultText: {
220+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-default-text',
221+
defaultMessage: 'Section settings cannot be configured within Libraries and must be set within a course. In a future release, Libraries may support configuring some settings.',
222+
description: 'Settings section tab default text',
223+
},
224+
settingsSubSectionDefaultText: {
225+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-subsection-default-text',
226+
defaultMessage: 'Subsection settings cannot be configured within Libraries and must be set within a course. In a future release, Libraries may support configuring some settings.',
227+
description: 'Settings subsection tab default text',
228+
},
229+
settingsUnitDefaultText: {
230+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-unit-default-text',
231+
defaultMessage: 'Unit settings cannot be configured within Libraries and must be set within a course. In a future release, Libraries may support configuring some settings',
232+
description: 'Settings unit tab default text',
233+
},
234+
settingsSectionGradingLabel: {
235+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-grading-label',
236+
defaultMessage: 'Subsection Grading',
237+
description: 'Label for the grading section in settings',
238+
},
239+
settingsSectionVisibilityLabel: {
240+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-visibility-label',
241+
defaultMessage: 'Visibility',
242+
description: 'Label for the visibility in settings',
243+
},
244+
settingsSectionAssessmentResultsVisibilityLabel: {
245+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-assessment-results-visibility-label',
246+
defaultMessage: 'Assessment Results Visibility',
247+
description: 'Label for the Assessment Results Visibility in settings',
248+
},
249+
settingsSectionUpgradeButton: {
250+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-upgrade-button',
251+
defaultMessage: 'Upgraded',
252+
description: 'Label for the upgrade button in settings',
253+
},
254+
settingsSectionGradeButton: {
255+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-grade-button',
256+
defaultMessage: 'Graded',
257+
description: 'Label for the grade button in settings',
258+
},
259+
settingsSectionDefaultVisibilityButton: {
260+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-default-visibility-button',
261+
defaultMessage: 'Default Visibility',
262+
description: 'Label for the default visibility button in settings',
263+
},
264+
settingsSectionStaffOnlyButton: {
265+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-staff-only-button',
266+
defaultMessage: 'Staff Only',
267+
description: 'Label for the staff only button in settings',
268+
},
269+
settingsSectionShowButton: {
270+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-show-button',
271+
defaultMessage: 'Show',
272+
description: 'Label for the show button in settings',
273+
},
274+
settingsSectionHideButton: {
275+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-hide-button',
276+
defaultMessage: 'Hide',
277+
description: 'Label for the hide button in settings',
278+
},
279+
settingsSectionHideContentAfterDueDateLabel: {
280+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-hide-content-after-due-date',
281+
defaultMessage: 'Hide content after due date',
282+
description: 'Label for the hide content after due date checkbox in settings',
283+
},
284+
settingsSectionOnlyShowResultsAfterDueDateLabel: {
285+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-only-show-results-after-due-date',
286+
defaultMessage: 'Only show results after due date',
287+
description: 'Label for the only show results after due date checkbox in settings',
288+
},
289+
settingsSectionDiscussionLabel: {
290+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-discussion-label',
291+
defaultMessage: 'Discussion',
292+
description: 'Label for the discussion in settings',
293+
},
294+
settingsSectionEnableDiscussionLabel: {
295+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-enable-discussion-label',
296+
defaultMessage: 'Enable Discussion',
297+
description: 'Label for the enable discussion checkbox in settings',
298+
},
299+
settingsSectionUnpublishedUnitsLabel: {
300+
id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-unpublished-units-label',
301+
defaultMessage: 'Topics for unpublished units will not be created',
302+
description: 'Label for the unpublished units in settings',
303+
},
219304
});
220305

221306
export default messages;

0 commit comments

Comments
 (0)