diff --git a/src/library-authoring/containers/ContainerInfo.tsx b/src/library-authoring/containers/ContainerInfo.tsx index 2b36a0764d..e4c89e9030 100644 --- a/src/library-authoring/containers/ContainerInfo.tsx +++ b/src/library-authoring/containers/ContainerInfo.tsx @@ -2,13 +2,13 @@ import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n'; import { Button, Stack, - Tab, - Tabs, Dropdown, Icon, IconButton, useToggle, } from '@openedx/paragon'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { Tab, Nav } from 'react-bootstrap'; import React, { useCallback } from 'react'; import { Link } from 'react-router-dom'; import { MoreVert } from '@openedx/paragon/icons'; @@ -24,6 +24,7 @@ import { } from '../common/context/SidebarContext'; import ContainerOrganize from './ContainerOrganize'; import ContainerUsage from './ContainerUsage'; +import { SettingsPanel } from './SettingsPanel'; import { useLibraryRoutes } from '../routes'; import { LibraryUnitBlocks } from '../units/LibraryUnitBlocks'; import { LibraryContainerChildren } from '../section-subsections/LibraryContainerChildren'; @@ -39,7 +40,6 @@ type ContainerPreviewProps = { const ContainerMenu = ({ containerId }: ContainerPreviewProps) => { const intl = useIntl(); - const [isConfirmingDelete, confirmDelete, cancelDelete] = useToggle(false); return ( @@ -159,23 +159,21 @@ const ContainerInfo = () => { sidebarTab && isContainerInfoTab(sidebarTab) ) ? sidebarTab : defaultContainerTab; - /* istanbul ignore next */ const handleTabChange = (newTab: ContainerInfoTab) => { resetSidebarAction(); setSidebarTab(newTab); }; - const renderTab = useCallback((infoTab: ContainerInfoTab, title: string, component?: React.ReactNode) => { + const renderTab = useCallback((infoTab: ContainerInfoTab, title: string) => { if (hiddenTabs.includes(infoTab)) { - // For some reason, returning anything other than empty list breaks the tab style - return []; + return null; } return ( - - {component} - + + {title} + ); - }, [hiddenTabs, defaultContainerTab, containerId]); + }, [hiddenTabs]); if (!container || !containerId || !containerType) { return null; @@ -188,34 +186,50 @@ const ContainerInfo = () => { containerType={containerType} hasUnpublishedChanges={container.hasUnpublishedChanges} /> - handleTabChange(k as ContainerInfoTab)} + mountOnEnter + unmountOnExit > - {renderTab( - CONTAINER_INFO_TABS.Preview, - intl.formatMessage(messages.previewTabTitle), - , - )} - {renderTab( - CONTAINER_INFO_TABS.Manage, - intl.formatMessage(messages.manageTabTitle), - , - )} - {renderTab( - CONTAINER_INFO_TABS.Usage, - intl.formatMessage(messages.usageTabTitle), - , - )} - {renderTab( - CONTAINER_INFO_TABS.Settings, - intl.formatMessage(messages.settingsTabTitle), - // TODO: container settings component - )} - + + + + + + + + + + + + + + + + + ); }; diff --git a/src/library-authoring/containers/SettingsPanel.test.tsx b/src/library-authoring/containers/SettingsPanel.test.tsx new file mode 100644 index 0000000000..e67f9c1a51 --- /dev/null +++ b/src/library-authoring/containers/SettingsPanel.test.tsx @@ -0,0 +1,122 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { ContainerType } from '@src/generic/key-utils'; +import { SettingsPanel } from './SettingsPanel'; +import messages from './messages'; + +const renderWithIntl = (ui: React.ReactNode) => render( + + {ui} + , +); + +describe('SettingsPanel', () => { + describe('Section container', () => { + test('renders section default info text', () => { + renderWithIntl(); + expect( + screen.getByText(messages.settingsSectionDefaultText.defaultMessage), + ).toBeInTheDocument(); + }); + + test('does not render grading or results visibility', () => { + renderWithIntl(); + expect( + screen.queryByText(messages.settingsSectionGradingLabel.defaultMessage), + ).not.toBeInTheDocument(); + expect( + screen.queryByText( + messages.settingsSectionAssessmentResultsVisibilityLabel.defaultMessage, + ), + ).not.toBeInTheDocument(); + }); + + test('renders visibility controls', () => { + renderWithIntl(); + expect( + screen.getByText(messages.settingsSectionVisibilityLabel.defaultMessage), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { + name: messages.settingsSectionDefaultVisibilityButton.defaultMessage, + }), + ).toBeDisabled(); + }); + }); + + describe('Subsection container', () => { + test('renders subsection default info text', () => { + renderWithIntl(); + expect( + screen.getByText(messages.settingsSubSectionDefaultText.defaultMessage), + ).toBeInTheDocument(); + }); + + test('renders grading buttons (disabled)', () => { + renderWithIntl(); + expect( + screen.getByRole('button', { + name: messages.settingsSectionUpgradeButton.defaultMessage, + }), + ).toBeDisabled(); + expect( + screen.getByRole('button', { + name: messages.settingsSectionGradeButton.defaultMessage, + }), + ).toBeDisabled(); + }); + + test('renders visibility + hide content checkbox', () => { + renderWithIntl(); + expect( + screen.getByLabelText( + messages.settingsSectionHideContentAfterDueDateLabel.defaultMessage, + ), + ).toBeDisabled(); + }); + + test('renders results visibility controls', () => { + renderWithIntl(); + expect( + screen.getByRole('button', { + name: messages.settingsSectionShowButton.defaultMessage, + }), + ).toBeDisabled(); + expect( + screen.getByRole('button', { + name: messages.settingsSectionHideButton.defaultMessage, + }), + ).toBeDisabled(); + expect( + screen.getByLabelText( + messages.settingsSectionOnlyShowResultsAfterDueDateLabel.defaultMessage, + ), + ).toBeDisabled(); + }); + }); + + describe('Unit container', () => { + test('renders unit default info text', () => { + renderWithIntl(); + expect( + screen.getByText(messages.settingsUnitDefaultText.defaultMessage), + ).toBeInTheDocument(); + }); + + test('renders discussion settings', () => { + renderWithIntl(); + expect( + screen.getByText(messages.settingsSectionDiscussionLabel.defaultMessage), + ).toBeInTheDocument(); + expect( + screen.getByLabelText( + messages.settingsSectionEnableDiscussionLabel.defaultMessage, + ), + ).toBeChecked(); + expect( + screen.getByText(messages.settingsSectionUnpublishedUnitsLabel.defaultMessage), + ).toBeInTheDocument(); + }); + }); +}); diff --git a/src/library-authoring/containers/SettingsPanel.tsx b/src/library-authoring/containers/SettingsPanel.tsx new file mode 100644 index 0000000000..54f44d5a5e --- /dev/null +++ b/src/library-authoring/containers/SettingsPanel.tsx @@ -0,0 +1,131 @@ +import React, { useState } from 'react'; +import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { Button, ButtonGroup, Form } from '@openedx/paragon'; +import { ContainerType } from '@src/generic/key-utils'; +import messages from './messages'; + +interface SettingsPanelProps { + containerType: string; +} + +export const SettingsPanel: React.FC = ({ containerType }) => { + const [grading] = useState('ungraded'); + const [visibility] = useState('default'); + const [resultsVisibility] = useState('show'); + + const disableAll = true; // 👈 set to false to re-enable + + return ( + <> +
+

+ { containerType === ContainerType.Section && ( + + )} + { containerType === ContainerType.Subsection && ( + + )} + { containerType === ContainerType.Unit && ( + + )} +

+
+
+ {containerType === ContainerType.Subsection && ( + <> +
+ +
+ + + + + + )} + +
+ +
+ + + + + {containerType === ContainerType.Subsection && ( + + + + )} + + {containerType === ContainerType.Subsection && ( + <> +
+ +
+ + + + + + + + + )} + {containerType === ContainerType.Unit && ( + <> +
+ +
+ + + +

+ +

+ + )} +
+ + ); +}; diff --git a/src/library-authoring/containers/messages.ts b/src/library-authoring/containers/messages.ts index 0cd961a121..0661587a5c 100644 --- a/src/library-authoring/containers/messages.ts +++ b/src/library-authoring/containers/messages.ts @@ -216,6 +216,91 @@ const messages = defineMessages({ defaultMessage: 'Failed to publish changes', description: 'Popup text seen if publishing a container fails', }, + settingsSectionDefaultText: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-default-text', + defaultMessage: 'Section settings can not be configured within Libraries and must be set within a course. In a future release, Libraries may support configuring some settings.', + description: 'Settings section tab default text', + }, + settingsSubSectionDefaultText: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-subsection-default-text', + defaultMessage: 'Subsection settings can not be configured within Libraries and must be set within a course. In a future release, Libraries may support configuring some settings.', + description: 'Settings subsection tab default text', + }, + settingsUnitDefaultText: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-unit-default-text', + defaultMessage: 'This unit was imported from a course. When you reuse this section, you can configure the settings locally. In a future release, Content Libraries may support configuring some settings.', + description: 'Settings unit tab default text', + }, + settingsSectionGradingLabel: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-grading-label', + defaultMessage: 'Subsection Grading', + description: 'Label for the grading section in settings', + }, + settingsSectionVisibilityLabel: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-visibility-label', + defaultMessage: 'Visibility', + description: 'Label for the visibility in settings', + }, + settingsSectionAssessmentResultsVisibilityLabel: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-assessment-results-visibility-label', + defaultMessage: 'Assessment Results Visibility', + description: 'Label for the Assessment Results Visibility in settings', + }, + settingsSectionUpgradeButton: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-upgrade-button', + defaultMessage: 'Upgrade', + description: 'Label for the upgrade button in settings', + }, + settingsSectionGradeButton: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-grade-button', + defaultMessage: 'Grade', + description: 'Label for the grade button in settings', + }, + settingsSectionDefaultVisibilityButton: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-default-visibility-button', + defaultMessage: 'Default Visibility', + description: 'Label for the default visibility button in settings', + }, + settingsSectionStaffOnlyButton: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-staff-only-button', + defaultMessage: 'Staff Only', + description: 'Label for the staff only button in settings', + }, + settingsSectionShowButton: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-show-button', + defaultMessage: 'Show', + description: 'Label for the show button in settings', + }, + settingsSectionHideButton: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-hide-button', + defaultMessage: 'Hide', + description: 'Label for the hide button in settings', + }, + settingsSectionHideContentAfterDueDateLabel: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-hide-content-after-due-date', + defaultMessage: 'Hide content after due date', + description: 'Label for the hide content after due date checkbox in settings', + }, + settingsSectionOnlyShowResultsAfterDueDateLabel: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-only-show-results-after-due-date', + defaultMessage: 'Only show results after due date', + description: 'Label for the only show results after due date checkbox in settings', + }, + settingsSectionDiscussionLabel: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-discussion-label', + defaultMessage: 'Discussion', + description: 'Label for the discussion in settings', + }, + settingsSectionEnableDiscussionLabel: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-enable-discussion-label', + defaultMessage: 'Enable Discussion', + description: 'Label for the enable discussion checkbox in settings', + }, + settingsSectionUnpublishedUnitsLabel: { + id: 'course-authoring.library-authoring.container-sidebar.publisher.settings-section-unpublished-units-label', + defaultMessage: 'Topics for unpublished units will not be created', + description: 'Label for the unpublished units in settings', + }, }); export default messages;