diff --git a/catalog-info.yaml b/catalog-info.yaml index 9eec06ff6c..9fa7a240ea 100644 --- a/catalog-info.yaml +++ b/catalog-info.yaml @@ -12,6 +12,7 @@ metadata: icon: "Web" annotations: openedx.org/arch-interest-groups: "" + openedx.org/release: "master" spec: owner: group:2u-tnl type: 'website' diff --git a/openedx.yaml b/openedx.yaml deleted file mode 100644 index 4e57270214..0000000000 --- a/openedx.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# This file describes this Open edX repo, as described in OEP-2: -# http://open-edx-proposals.readthedocs.io/en/latest/oeps/oep-0002.html#specification - -nick: cath -oeps: {} -owner: edx/platform-core-tnl -openedx-release: - # The openedx-release key is described in OEP-10: - # https://open-edx-proposals.readthedocs.io/en/latest/oep-0010-proc-openedx-releases.html - # The FAQ might also be helpful: https://openedx.atlassian.net/wiki/spaces/COMM/pages/1331268879/Open+edX+Release+FAQ - ref: master diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap index 0fcfc6ed06..8ae716816f 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap @@ -1,5 +1,188 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`SettingsWidget isLibrary snapshot: renders Settings widget for Advanced Problem with correct widgets 1`] = ` +
+
+ +
+
+ +
+ +
+ + + + + +
+ + +
+ +
+ +
+
+`; + +exports[`SettingsWidget isLibrary snapshot: renders Settings widget page 1`] = ` +
+
+ +
+
+ +
+ +
+ + + + + +
+ + +
+ +
+ +
+
+`; + +exports[`SettingsWidget isLibrary snapshot: renders Settings widget page advanced settings visible 1`] = ` +
+
+ +
+
+ +
+ +
+ + + + + +
+ + +
+ +
+ +
+
+`; + exports[`SettingsWidget snapshot snapshot: renders Settings widget for Advanced Problem with correct widgets 1`] = `
)} -
- -
+ {!isLibrary && ( +
+ +
+ )}
- -
- -
-
- -
+ {!isLibrary && ( +
+ +
+ )} + {!isLibrary && ( +
+ +
+ )} { problemType === ProblemTypeKeys.ADVANCED && (
@@ -142,9 +147,11 @@ const SettingsWidget = ({
) } -
- -
+ {!isLibrary && ( +
+ +
+ )}
diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx index 1630753c05..db7834492a 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx @@ -74,6 +74,39 @@ describe('SettingsWidget', () => { }); }); + describe('isLibrary', () => { + const libraryProps = { + ...props, + isLibrary: true, + }; + test('snapshot: renders Settings widget page', () => { + const showAdvancedSettingsCardsProps = { + isAdvancedCardsVisible: false, + setResetTrue: jest.fn().mockName('showAdvancedSettingsCards.setResetTrue'), + }; + showAdvancedSettingsCards.mockReturnValue(showAdvancedSettingsCardsProps); + expect(shallow().snapshot).toMatchSnapshot(); + }); + test('snapshot: renders Settings widget page advanced settings visible', () => { + const showAdvancedSettingsCardsProps = { + isAdvancedCardsVisible: true, + setResetTrue: jest.fn().mockName('showAdvancedSettingsCards.setResetTrue'), + }; + showAdvancedSettingsCards.mockReturnValue(showAdvancedSettingsCardsProps); + expect(shallow().snapshot).toMatchSnapshot(); + }); + test('snapshot: renders Settings widget for Advanced Problem with correct widgets', () => { + const showAdvancedSettingsCardsProps = { + isAdvancedCardsVisible: true, + setResetTrue: jest.fn().mockName('showAdvancedSettingsCards.setResetTrue'), + }; + showAdvancedSettingsCards.mockReturnValue(showAdvancedSettingsCardsProps); + expect(shallow( + , + ).snapshot).toMatchSnapshot(); + }); + }); + describe('mapDispatchToProps', () => { test('setBlockTitle from actions.app.setBlockTitle', () => { expect(mapDispatchToProps.setBlockTitle).toEqual(actions.app.setBlockTitle); diff --git a/src/grading-settings/grading-scale/GradingScale.scss b/src/grading-settings/grading-scale/GradingScale.scss index 8ca6ac9ec3..1d65f7ceff 100644 --- a/src/grading-settings/grading-scale/GradingScale.scss +++ b/src/grading-settings/grading-scale/GradingScale.scss @@ -6,7 +6,7 @@ .grading-scale-segments-and-ticks { display: inline-block; - height: 3.125rem; + height: 3.5rem; width: 100%; border: 1px solid $black; overflow: hidden; @@ -70,13 +70,9 @@ } .grading-scale-segment-btn-remove { - font: normal $font-weight-normal .6875rem/1 $font-family-base; + font-size: x-small; display: none; - position: absolute; - top: -1.5625rem; - right: -.375rem; - padding: .375rem; - text-decoration: none; + margin: -2px -4px 0 0; } .grading-scale-segment-content { diff --git a/src/grading-settings/grading-scale/components/GradingScaleSegment.tsx b/src/grading-settings/grading-scale/components/GradingScaleSegment.tsx index 9cc3e059d1..95d95b781a 100644 --- a/src/grading-settings/grading-scale/components/GradingScaleSegment.tsx +++ b/src/grading-settings/grading-scale/components/GradingScaleSegment.tsx @@ -66,19 +66,19 @@ const GradingScaleSegment = ({ {gradingSegments[idx === 0 ? 0 : idx - 1]?.previous} - {value === 100 ? value : value - 1} + {idx !== gradingSegments.length && idx - 1 !== 0 && ( + + )} - {idx !== gradingSegments.length && idx - 1 !== 0 && ( - - )} ); }; diff --git a/src/index.jsx b/src/index.jsx index 34f27f1b9a..c885dac167 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -35,7 +35,13 @@ import { ToastProvider } from './generic/toast-context'; import 'react-datepicker/dist/react-datepicker.css'; import './index.scss'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 60_000, // If cache is up to one hour old, no need to re-fetch + }, + }, +}); const App = () => { useEffect(() => { diff --git a/src/library-authoring/LibraryAuthoringPage.test.tsx b/src/library-authoring/LibraryAuthoringPage.test.tsx index 2b55375fd2..ed02d68bd1 100644 --- a/src/library-authoring/LibraryAuthoringPage.test.tsx +++ b/src/library-authoring/LibraryAuthoringPage.test.tsx @@ -111,6 +111,10 @@ describe('', () => { expect(screen.getAllByText('Recently Modified').length).toEqual(1); expect((await screen.findAllByText('Introduction to Testing'))[0]).toBeInTheDocument(); + // Search box should not have focus on page load + const searchBox = screen.getByRole('searchbox'); + expect(searchBox).not.toHaveFocus(); + // Navigate to the components tab fireEvent.click(screen.getByRole('tab', { name: 'Components' })); // "Recently Modified" default sort shown diff --git a/src/library-authoring/LibraryLayout.tsx b/src/library-authoring/LibraryLayout.tsx index c093af7ad3..add33f95a0 100644 --- a/src/library-authoring/LibraryLayout.tsx +++ b/src/library-authoring/LibraryLayout.tsx @@ -2,11 +2,12 @@ import { useCallback } from 'react'; import { Route, Routes, + useMatch, useParams, - useLocation, + type PathMatch, } from 'react-router-dom'; -import { ROUTES } from './routes'; +import { BASE_ROUTE, ROUTES } from './routes'; import LibraryAuthoringPage from './LibraryAuthoringPage'; import { LibraryProvider } from './common/context/LibraryContext'; import { SidebarProvider } from './common/context/SidebarContext'; @@ -23,12 +24,15 @@ const LibraryLayout = () => { throw new Error('Error: route is missing libraryId.'); } - const location = useLocation(); + // The top-level route is `${BASE_ROUTE}/*`, so match will always be non-null. + const match = useMatch(`${BASE_ROUTE}${ROUTES.COLLECTION}`) as PathMatch<'libraryId' | 'collectionId'> | null; + const collectionId = match?.params.collectionId; + const context = useCallback((childPage) => ( { - ), [location.pathname]); + ), [collectionId]); return ( diff --git a/src/library-authoring/collections/CollectionDetails.tsx b/src/library-authoring/collections/CollectionDetails.tsx index d6b4f168cf..4594ed58ae 100644 --- a/src/library-authoring/collections/CollectionDetails.tsx +++ b/src/library-authoring/collections/CollectionDetails.tsx @@ -166,8 +166,7 @@ const CollectionDetails = () => { {intl.formatMessage(messages.detailsTabHistoryTitle)} diff --git a/src/library-authoring/component-info/ComponentDetails.test.tsx b/src/library-authoring/component-info/ComponentDetails.test.tsx index 33fb4c211a..8278b3727c 100644 --- a/src/library-authoring/component-info/ComponentDetails.test.tsx +++ b/src/library-authoring/component-info/ComponentDetails.test.tsx @@ -53,10 +53,12 @@ describe('', () => { }); it('should render the component history', async () => { - render(mockLibraryBlockMetadata.usageKeyNeverPublished); + render(mockLibraryBlockMetadata.usageKeyPublished); // Show created date expect(await screen.findByText('June 20, 2024')).toBeInTheDocument(); // Show modified date expect(await screen.findByText('June 21, 2024')).toBeInTheDocument(); + // Show last published date + expect(await screen.findByText('June 22, 2024')).toBeInTheDocument(); }); }); diff --git a/src/library-authoring/component-info/ComponentManagement.test.tsx b/src/library-authoring/component-info/ComponentManagement.test.tsx index b9f9e0a7c9..6c1cdc0b6e 100644 --- a/src/library-authoring/component-info/ComponentManagement.test.tsx +++ b/src/library-authoring/component-info/ComponentManagement.test.tsx @@ -68,7 +68,7 @@ describe('', () => { expect(await screen.findByText('Published')).toBeInTheDocument(); expect(screen.getByText('Published')).toBeInTheDocument(); expect( - screen.getByText(matchInnerText('SPAN', 'Last published on June 21, 2024 at 24:00 by Luke.')), + screen.getByText(matchInnerText('SPAN', 'Last published on June 22, 2024 at 24:00 by Luke.')), ).toBeInTheDocument(); }); diff --git a/src/library-authoring/data/api.mocks.ts b/src/library-authoring/data/api.mocks.ts index 0e064f8a0f..ce4be29168 100644 --- a/src/library-authoring/data/api.mocks.ts +++ b/src/library-authoring/data/api.mocks.ts @@ -356,7 +356,7 @@ mockLibraryBlockMetadata.dataPublished = { defKey: null, blockType: 'html', displayName: 'Introduction to Testing 2', - lastPublished: '2024-06-21T00:00:00', + lastPublished: '2024-06-22T00:00:00', publishedBy: 'Luke', lastDraftCreated: null, lastDraftCreatedBy: '2024-06-20T20:00:00Z', diff --git a/src/library-authoring/generic/history-widget/index.tsx b/src/library-authoring/generic/history-widget/index.tsx index 8b49d03d77..7f63b15842 100644 --- a/src/library-authoring/generic/history-widget/index.tsx +++ b/src/library-authoring/generic/history-widget/index.tsx @@ -15,6 +15,7 @@ const CustomFormattedDate = ({ date }: { date: string | Date }) => ( type HistoryWidgedProps = { modified: string | Date | null; created: string | Date | null; + lastPublished?: string | Date | null; }; /** @@ -32,8 +33,15 @@ type HistoryWidgedProps = { const HistoryWidget = ({ modified, created, + lastPublished, }: HistoryWidgedProps) => ( + {lastPublished && ( +
+
+ +
+ )} {modified && (
diff --git a/src/library-authoring/generic/history-widget/messages.ts b/src/library-authoring/generic/history-widget/messages.ts index b0c84a85e7..4b0176a90c 100644 --- a/src/library-authoring/generic/history-widget/messages.ts +++ b/src/library-authoring/generic/history-widget/messages.ts @@ -1,6 +1,11 @@ import { defineMessages } from '@edx/frontend-platform/i18n'; const messages = defineMessages({ + lastPublishedTitle: { + id: 'course-authoring.library-authoring.generic.history-widget.last-published', + defaultMessage: 'Last Published', + description: 'Title of the last published section in the library authoring sidebar.', + }, lastModifiedTitle: { id: 'course-authoring.library-authoring.generic.history-widget.last-modified', defaultMessage: 'Last Modified', diff --git a/src/search-manager/SearchKeywordsField.tsx b/src/search-manager/SearchKeywordsField.tsx index 14a6a06dc9..90c09fdd93 100644 --- a/src/search-manager/SearchKeywordsField.tsx +++ b/src/search-manager/SearchKeywordsField.tsx @@ -7,7 +7,11 @@ import { useSearchContext } from './SearchManager'; /** * The "main" input field where users type in search keywords. The search happens as they type (no need to press enter). */ -const SearchKeywordsField: React.FC<{ className?: string, placeholder?: string }> = (props) => { +const SearchKeywordsField: React.FC<{ + className?: string, + placeholder?: string, + autoFocus?: boolean, +}> = (props) => { const intl = useIntl(); const { searchKeywords, setSearchKeywords, usageKey } = useSearchContext(); const defaultPlaceholder = usageKey ? messages.clearUsageKeyToSearch : messages.inputPlaceholder; @@ -24,7 +28,7 @@ const SearchKeywordsField: React.FC<{ className?: string, placeholder?: string } > diff --git a/src/search-manager/data/api.ts b/src/search-manager/data/api.ts index 0763000f55..28822071a6 100644 --- a/src/search-manager/data/api.ts +++ b/src/search-manager/data/api.ts @@ -473,9 +473,9 @@ export async function fetchTagsThatMatchKeyword({ attributesToSearchOn: ['tags.taxonomy', 'tags.level0', 'tags.level1', 'tags.level2', 'tags.level3'], attributesToRetrieve: ['tags'], limit, - // We'd like to use 'showMatchesPosition: true' to know exactly which tags match, but it doesn't provide the - // detail we need; it's impossible to tell which tag at a given level matched based on the returned _matchesPosition - // data - https://github.com/orgs/meilisearch/discussions/550 + // TODO: improve this - use 'showMatchesPosition: true' to know exactly which tags match. Previously it didn't + // provide the detail we need (https://github.com/orgs/meilisearch/discussions/550) but it has now been implemented + // in newer versions of Meilisearch. See https://github.com/meilisearch/meilisearch/pull/5005 which fixes it. }); const tagSearchKeywordsLower = tagSearchKeywords.toLocaleLowerCase(); diff --git a/src/search-modal/SearchModal.test.tsx b/src/search-modal/SearchModal.test.tsx index ef35726395..c7884fbf8c 100644 --- a/src/search-modal/SearchModal.test.tsx +++ b/src/search-modal/SearchModal.test.tsx @@ -5,7 +5,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import { AppProvider } from '@edx/frontend-platform/react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import type { Store } from 'redux'; import MockAdapter from 'axios-mock-adapter'; @@ -73,4 +73,14 @@ describe('', () => { const { findByText } = render(); expect(await findByText('An error occurred. Unable to load search results.')).toBeInTheDocument(); }); + + it('should set focus on the search input box when loaded in the modal', async () => { + axiosMock.onGet(getContentSearchConfigUrl()).replyOnce(200, { + url: 'https://meilisearch.example.com', + index: 'test-index', + apiKey: 'test-api-key', + }); + render(); + expect(screen.getByRole('searchbox')).toHaveFocus(); + }); }); diff --git a/src/search-modal/SearchModal.tsx b/src/search-modal/SearchModal.tsx index 2e552fb6e8..197ba6c708 100644 --- a/src/search-modal/SearchModal.tsx +++ b/src/search-modal/SearchModal.tsx @@ -21,7 +21,11 @@ const SearchModal: React.FC<{ courseId?: string, isOpen: boolean, onClose: () => isFullscreenOnMobile className="courseware-search-modal" > - + ); }; diff --git a/src/search-modal/SearchUI.tsx b/src/search-modal/SearchUI.tsx index 2df074111c..a70e1f69fe 100644 --- a/src/search-modal/SearchUI.tsx +++ b/src/search-modal/SearchUI.tsx @@ -19,7 +19,11 @@ import EmptyStates from './EmptyStates'; import SearchResults from './SearchResults'; import messages from './messages'; -const SearchUI: React.FC<{ courseId?: string, closeSearchModal?: () => void }> = (props) => { +const SearchUI: React.FC<{ + courseId?: string, + autoFocus?: boolean, + closeSearchModal?: () => void, +}> = (props) => { const hasCourseId = Boolean(props.courseId); const [searchThisCourseEnabled, setSearchThisCourse] = React.useState(hasCourseId); const switchToThisCourse = React.useCallback(() => setSearchThisCourse(true), []); @@ -39,7 +43,10 @@ const SearchUI: React.FC<{ courseId?: string, closeSearchModal?: () => void }> =
- +