From dc5308dc92bc7e732bbe6e913e4fe6cb03c3d905 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B4mulo=20Penido?= Date: Thu, 19 Sep 2024 14:12:36 -0300 Subject: [PATCH] feat: add ComponentDetails component --- .../component-info/ComponentDetails.test.tsx | 28 ++++++++++ .../component-info/ComponentDetails.tsx | 52 +++++++++++++++++++ .../component-info/ComponentDeveloperInfo.tsx | 2 +- .../component-info/ComponentInfo.tsx | 8 +-- .../ComponentManagement.test.tsx | 2 +- .../component-info/messages.ts | 15 ++++++ src/library-authoring/data/api.mocks.ts | 5 ++ src/library-authoring/data/api.ts | 1 + .../generic/history-widget/HistoryWidget.scss | 6 +++ .../generic/history-widget/index.tsx | 52 +++++++++++++++++++ .../generic/history-widget/messages.ts | 16 ++++++ src/library-authoring/generic/index.scss | 1 + 12 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 src/library-authoring/component-info/ComponentDetails.test.tsx create mode 100644 src/library-authoring/component-info/ComponentDetails.tsx create mode 100644 src/library-authoring/generic/history-widget/HistoryWidget.scss create mode 100644 src/library-authoring/generic/history-widget/index.tsx create mode 100644 src/library-authoring/generic/history-widget/messages.ts diff --git a/src/library-authoring/component-info/ComponentDetails.test.tsx b/src/library-authoring/component-info/ComponentDetails.test.tsx new file mode 100644 index 0000000000..48c0d9827b --- /dev/null +++ b/src/library-authoring/component-info/ComponentDetails.test.tsx @@ -0,0 +1,28 @@ +import { + initializeMocks, + render, + screen, +} from '../../testUtils'; +import { mockLibraryBlockMetadata } from '../data/api.mocks'; +import ComponentDetails from './ComponentDetails'; + +describe('', () => { + it('should render the component usage', async () => { + initializeMocks(); + mockLibraryBlockMetadata.applyMock(); + render(); + expect(await screen.findByText('Component Usage')).toBeInTheDocument(); + // TODO: replace with actual data when implement tag list + expect(screen.queryByText('This will show the courses that use this component.')).toBeInTheDocument(); + }); + + it('should render the component history', async () => { + initializeMocks(); + mockLibraryBlockMetadata.applyMock(); + render(); + // Show created date + expect(await screen.findByText('June 20, 2024')).toBeInTheDocument(); + // Show modified date + expect(await screen.findByText('June 21, 2024')).toBeInTheDocument(); + }); +}); diff --git a/src/library-authoring/component-info/ComponentDetails.tsx b/src/library-authoring/component-info/ComponentDetails.tsx new file mode 100644 index 0000000000..3504df9b5b --- /dev/null +++ b/src/library-authoring/component-info/ComponentDetails.tsx @@ -0,0 +1,52 @@ +import { useIntl } from '@edx/frontend-platform/i18n'; +import { Stack } from '@openedx/paragon'; + +import { useLibraryBlockMetadata } from '../data/apiHooks'; +import HistoryWidget from '../generic/history-widget'; +import { ComponentDeveloperInfo } from './ComponentDeveloperInfo'; +import messages from './messages'; + +interface ComponentDetailsProps { + usageKey: string; +} + +const ComponentDetails = ({ usageKey }: ComponentDetailsProps) => { + const intl = useIntl(); + const { data: componentMetadata } = useLibraryBlockMetadata(usageKey); + + // istanbul ignore if: this should never happen + if (!componentMetadata) { + return null; + } + + return ( + +
+

+ {intl.formatMessage(messages.detailsTabDescriptionTitle)} +

+ +
+
+

+ {intl.formatMessage(messages.detailsTabUsageTitle)} +

+ This will show the courses that use this component. +
+
+
+

+ {intl.formatMessage(messages.detailsTabHistoryTitle)} +

+ +
+ { + (process.env.NODE_ENV === 'development' ? : null) + } +
+ ); +}; + +export default ComponentDetails; diff --git a/src/library-authoring/component-info/ComponentDeveloperInfo.tsx b/src/library-authoring/component-info/ComponentDeveloperInfo.tsx index 430d9a7636..8e73d1fdfb 100644 --- a/src/library-authoring/component-info/ComponentDeveloperInfo.tsx +++ b/src/library-authoring/component-info/ComponentDeveloperInfo.tsx @@ -14,7 +14,7 @@ export const ComponentDeveloperInfo: React.FC = ({ usageKey }) => { const { data: olx, isLoading: isOLXLoading } = useXBlockOLX(usageKey); return ( <> -
+

Developer Component Details

(This panel is only visible in development builds.)

diff --git a/src/library-authoring/component-info/ComponentInfo.tsx b/src/library-authoring/component-info/ComponentInfo.tsx index 735257d732..f503e4df99 100644 --- a/src/library-authoring/component-info/ComponentInfo.tsx +++ b/src/library-authoring/component-info/ComponentInfo.tsx @@ -10,7 +10,7 @@ import { Link } from 'react-router-dom'; import { getEditUrl } from '../components/utils'; import { ComponentMenu } from '../components'; -import { ComponentDeveloperInfo } from './ComponentDeveloperInfo'; +import ComponentDetails from './ComponentDetails'; import ComponentManagement from './ComponentManagement'; import ComponentPreview from './ComponentPreview'; import messages from './messages'; @@ -50,11 +50,7 @@ const ComponentInfo = ({ usageKey }: ComponentInfoProps) => { - Details tab placeholder - - { - (process.env.NODE_ENV === 'development' ? : null) - } + diff --git a/src/library-authoring/component-info/ComponentManagement.test.tsx b/src/library-authoring/component-info/ComponentManagement.test.tsx index b56d33bb0f..b069343016 100644 --- a/src/library-authoring/component-info/ComponentManagement.test.tsx +++ b/src/library-authoring/component-info/ComponentManagement.test.tsx @@ -9,7 +9,7 @@ import { mockLibraryBlockMetadata } from '../data/api.mocks'; import ComponentManagement from './ComponentManagement'; /* - * FIXME: Summarize the reason here + * This function is used to get the inner text of an element. * https://stackoverflow.com/questions/47902335/innertext-is-undefined-in-jest-test */ const getInnerText = (element: Element) => element?.textContent diff --git a/src/library-authoring/component-info/messages.ts b/src/library-authoring/component-info/messages.ts index 8a9404081e..8742fd0e08 100644 --- a/src/library-authoring/component-info/messages.ts +++ b/src/library-authoring/component-info/messages.ts @@ -51,6 +51,21 @@ const messages = defineMessages({ defaultMessage: 'Details', description: 'Title for details tab', }, + detailsTabDescriptionTitle: { + id: 'course-authoring.library-authoring.component.details-tab.description-title', + defaultMessage: 'Description / Card Preview Text', + description: 'Title for the Description container in the details tab', + }, + detailsTabUsageTitle: { + id: 'course-authoring.library-authoring.component.details-tab.usage-title', + defaultMessage: 'Component Usage', + description: 'Title for the Component Usage container in the details tab', + }, + detailsTabHistoryTitle: { + id: 'course-authoring.library-authoring.component.details-tab.history-title', + defaultMessage: 'Component History', + description: 'Title for the Component History container in the details tab', + }, previewExpandButtonTitle: { id: 'course-authoring.library-authoring.component.preview.expand.title', defaultMessage: 'Expand', diff --git a/src/library-authoring/data/api.mocks.ts b/src/library-authoring/data/api.mocks.ts index 0002f7516a..2dfb3dc7b9 100644 --- a/src/library-authoring/data/api.mocks.ts +++ b/src/library-authoring/data/api.mocks.ts @@ -134,6 +134,7 @@ mockCreateLibraryBlock.newHtmlData = { lastDraftCreated: '2024-07-22T21:37:49Z', lastDraftCreatedBy: null, created: '2024-07-22T21:37:49Z', + modified: '2024-07-22T21:37:49Z', tagsCount: 0, } satisfies api.LibraryBlockMetadata; mockCreateLibraryBlock.newProblemData = { @@ -147,6 +148,7 @@ mockCreateLibraryBlock.newProblemData = { lastDraftCreated: '2024-07-22T21:37:49Z', lastDraftCreatedBy: null, created: '2024-07-22T21:37:49Z', + modified: '2024-07-22T21:37:49Z', tagsCount: 0, } satisfies api.LibraryBlockMetadata; mockCreateLibraryBlock.newVideoData = { @@ -160,6 +162,7 @@ mockCreateLibraryBlock.newVideoData = { lastDraftCreated: '2024-07-22T21:37:49Z', lastDraftCreatedBy: null, created: '2024-07-22T21:37:49Z', + modified: '2024-07-22T21:37:49Z', tagsCount: 0, } satisfies api.LibraryBlockMetadata; /** Apply this mock. Returns a spy object that can tell you if it's been called. */ @@ -234,6 +237,7 @@ mockLibraryBlockMetadata.dataNeverPublished = { lastDraftCreatedBy: null, hasUnpublishedChanges: false, created: '2024-06-20T13:54:21Z', + modified: '2024-06-21T13:54:21Z', tagsCount: 0, } satisfies api.LibraryBlockMetadata; mockLibraryBlockMetadata.usageKeyPublished = 'lb:Axim:TEST2:html:571fe018-f3ce-45c9-8f53-5dafcb422fd2'; @@ -248,6 +252,7 @@ mockLibraryBlockMetadata.dataPublished = { lastDraftCreatedBy: '2024-06-20T20:00:00Z', hasUnpublishedChanges: false, created: '2024-06-20T13:54:21Z', + modified: '2024-06-21T13:54:21Z', tagsCount: 0, } satisfies api.LibraryBlockMetadata; /** Apply this mock. Returns a spy object that can tell you if it's been called. */ diff --git a/src/library-authoring/data/api.ts b/src/library-authoring/data/api.ts index 76388b1641..3eb61f9733 100644 --- a/src/library-authoring/data/api.ts +++ b/src/library-authoring/data/api.ts @@ -129,6 +129,7 @@ export interface LibraryBlockMetadata { lastDraftCreatedBy: string | null, hasUnpublishedChanges: boolean; created: string | null, + modified: string | null, tagsCount: number; } diff --git a/src/library-authoring/generic/history-widget/HistoryWidget.scss b/src/library-authoring/generic/history-widget/HistoryWidget.scss new file mode 100644 index 0000000000..84e11cf60a --- /dev/null +++ b/src/library-authoring/generic/history-widget/HistoryWidget.scss @@ -0,0 +1,6 @@ +.history-widget-bar { + border-left: 8px solid $info-300; + border-radius: 4px; + padding-left: 1rem; +} + diff --git a/src/library-authoring/generic/history-widget/index.tsx b/src/library-authoring/generic/history-widget/index.tsx new file mode 100644 index 0000000000..615a570504 --- /dev/null +++ b/src/library-authoring/generic/history-widget/index.tsx @@ -0,0 +1,52 @@ +import { FormattedMessage, FormattedDate } from '@edx/frontend-platform/i18n'; +import { Stack } from '@openedx/paragon'; + +import messages from './messages'; + +const CustomFormattedDate = ({ date }: { date: string }) => ( + +); + +type HistoryWidgedProps = { + modified: string | null; + created: string | null; +}; + +/** + * This component displays the history of an entity (Last Modified and Created dates) + * + * This component doesn't handle fetching the data or any other side effects. It only displays the dates. + * + * @example + * ```tsx + * const { data: componentMetadata } = useLibraryBlockMetadata(usageKey); + * + * return ; + * ``` + */ +const HistoryWidget = ({ + modified, + created, +}: HistoryWidgedProps) => ( + + {modified && ( +
+
+ +
+ )} + {created && ( +
+
+ +
+ )} +
+); + +export default HistoryWidget; diff --git a/src/library-authoring/generic/history-widget/messages.ts b/src/library-authoring/generic/history-widget/messages.ts new file mode 100644 index 0000000000..b0c84a85e7 --- /dev/null +++ b/src/library-authoring/generic/history-widget/messages.ts @@ -0,0 +1,16 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + lastModifiedTitle: { + id: 'course-authoring.library-authoring.generic.history-widget.last-modified', + defaultMessage: 'Last Modified', + description: 'Title of the last modified section in the library authoring sidebar.', + }, + createdTitle: { + id: 'course-authoring.library-authoring.generic.history-widget.created', + defaultMessage: 'Created', + description: 'Title of the created section in the library authoring sidebar.', + }, +}); + +export default messages; diff --git a/src/library-authoring/generic/index.scss b/src/library-authoring/generic/index.scss index 8e15b4671b..b7c9c75447 100644 --- a/src/library-authoring/generic/index.scss +++ b/src/library-authoring/generic/index.scss @@ -1 +1,2 @@ @import "./status-widget/StatusWidget"; +@import "./history-widget/HistoryWidget";