Skip to content

Commit 6363d70

Browse files
committed
feat: show container hierarchy with publish status
in "Confirm Publish" box. Refactors the ContainerPublishStatus component to use the container hierarchy API instead of the search engine. Plus fixes some styles.
1 parent 96d9445 commit 6363d70

File tree

11 files changed

+417
-219
lines changed

11 files changed

+417
-219
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
.content-hierarchy {
2+
margin-bottom: var(--pgn-spacing-paragraph-margin-bottom);
3+
4+
.hierarchy-row {
5+
border: 1px solid var(--pgn-color-light-500);
6+
border-radius: 4px;
7+
background-color: var(--pgn-color-white);
8+
padding: 0;
9+
margin: 0;
10+
11+
&.selected {
12+
border: 3px solid var(--pgn-color-primary-500);
13+
border-radius: 4px;
14+
}
15+
16+
.icon {
17+
background-color: var(--pgn-color-light-300);
18+
border-top: 2px solid var(--pgn-color-light-300);
19+
border-bottom: 2px solid var(--pgn-color-light-300);
20+
border-right: 1px solid var(--pgn-color-light-500);
21+
border-radius: 1px 0 0 1px;
22+
padding: 8px 12px;
23+
}
24+
25+
&.selected .icon {
26+
background-color: var(--pgn-color-primary-500);
27+
border-color: var(--pgn-color-primary-500);
28+
color: var(--pgn-color-white);
29+
}
30+
31+
.text {
32+
padding: 8px 12px;
33+
flex-grow: 2;
34+
}
35+
36+
.publish-status {
37+
background-color: var(--pgn-color-info-200);
38+
white-space: nowrap;
39+
padding: 8px 12px;
40+
}
41+
}
42+
43+
.hierarchy-arrow {
44+
color: var(--pgn-color-light-500);
45+
padding: 0 0 0 14px;
46+
position: relative;
47+
top: -4px;
48+
height: 20px;
49+
50+
&.selected {
51+
color: var(--pgn-color-primary-500);
52+
}
53+
}
54+
}

src/library-authoring/containers/ContainerHierarchy.tsx

Lines changed: 94 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { useIntl } from '@edx/frontend-platform/i18n';
1+
import type { MessageDescriptor } from 'react-intl';
2+
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
23
import { Container, Icon, Stack } from '@openedx/paragon';
3-
import { ArrowDownward } from '@openedx/paragon/icons';
4+
import { ArrowDownward, Check, Description } from '@openedx/paragon/icons';
45
import classNames from 'classnames';
56
import { getItemIcon } from '@src/generic/block-type-utils';
67
import Loading from '@src/generic/Loading';
7-
import NotFoundAlert from '@src/generic/NotFoundAlert';
88
import { ContainerType } from '@src/generic/key-utils';
9+
import type { ContainerHierarchyMember } from '../data/api';
910
import { useContainerHierarchy } from '../data/apiHooks';
1011
import { useSidebarContext } from '../common/context/SidebarContext';
1112
import messages from './messages';
@@ -14,44 +15,65 @@ const ContainerHierarchyRow = ({
1415
containerType,
1516
text,
1617
selected,
18+
showArrow,
19+
willPublish = false,
20+
publishMessage = undefined,
1721
}: {
1822
containerType: ContainerType,
1923
text: string,
2024
selected: boolean,
25+
showArrow: boolean,
26+
willPublish?: boolean,
27+
publishMessage?: MessageDescriptor,
2128
}) => (
2229
<Stack>
2330
<Container
24-
className={classNames('p-0 m-0 hierarchy-row', { selected })}
31+
className={classNames('hierarchy-row', { selected })}
2532
>
2633
<Stack
2734
direction="horizontal"
2835
gap={2}
29-
className="m-0 p-0"
3036
>
31-
<div className="p-2 icon">
37+
<div className="icon">
3238
<Icon
3339
src={getItemIcon(containerType)}
3440
screenReaderText={containerType}
3541
title={containerType}
3642
/>
3743
</div>
38-
<div className="p-2 text text-truncate">
44+
<div className="text text-truncate">
3945
{text}
4046
</div>
47+
{publishMessage && (
48+
<Stack
49+
direction="horizontal"
50+
gap={2}
51+
className="publish-status"
52+
>
53+
<Icon src={willPublish ? Check : Description} />
54+
<FormattedMessage {...(willPublish ? messages.willPublishChipText : publishMessage)} />
55+
</Stack>
56+
)}
4157
</Stack>
4258
</Container>
43-
<div
44-
className={classNames('hierarchy-arrow', { selected })}
45-
>
46-
<Icon
47-
src={ArrowDownward}
48-
screenReaderText={' '}
49-
/>
50-
</div>
59+
{showArrow && (
60+
<div
61+
className={classNames('hierarchy-arrow', { selected })}
62+
>
63+
<Icon
64+
src={ArrowDownward}
65+
screenReaderText={' '}
66+
/>
67+
</div>
68+
)}
5169
</Stack>
5270
);
5371

54-
const ContainerHierarchy = () => {
72+
const ContainerHierarchy = ({
73+
showPublishStatus = false,
74+
}: {
75+
showPublishStatus?: boolean,
76+
}) => {
5577
const intl = useIntl();
5678
const { sidebarItemInfo } = useSidebarContext();
5779
const containerId = sidebarItemInfo?.id;
@@ -71,8 +93,9 @@ const ContainerHierarchy = () => {
7193
return <Loading />;
7294
}
7395

96+
// istanbul ignore if: this should never happen
7497
if (isError) {
75-
return <NotFoundAlert />;
98+
return null;
7699
}
77100

78101
const {
@@ -82,9 +105,42 @@ const ContainerHierarchy = () => {
82105
components,
83106
} = data;
84107

108+
// Returns a message describing the publish status of the given hierarchy row.
109+
const publishMessage = (contents: ContainerHierarchyMember[]) => {
110+
// If we're not showing publish status, then we don't need a publish message
111+
if (!showPublishStatus) {
112+
return undefined;
113+
}
114+
115+
// If any item has unpublished changes, mark this row as Draft.
116+
if (contents.some((item) => item.hasUnpublishedChanges)) {
117+
return messages.draftChipText;
118+
}
119+
120+
// Otherwise, it's Published
121+
return messages.publishedChipText;
122+
};
123+
124+
// Returns True if any of the items in the list match the currently selected container.
125+
const selected = (contents: ContainerHierarchyMember[]): boolean => (
126+
contents.some((item) => item.id === containerId)
127+
);
128+
129+
// Use the "selected" status to determine the selected row.
130+
// If showPublishStatus, that row and its children will be marked "willPublish".
131+
const selectedSections = selected(sections);
132+
const selectedSubsections = selected(subsections);
133+
const selectedUnits = selected(units);
134+
const selectedComponents = selected(components);
135+
136+
const showSections = sections && sections.length > 0;
137+
const showSubsections = subsections && subsections.length > 0;
138+
const showUnits = units && units.length > 0;
139+
const showComponents = components && components.length > 0;
140+
85141
return (
86-
<Stack>
87-
{sections && sections.length > 0 && (
142+
<Stack className="content-hierarchy">
143+
{showSections && (
88144
<ContainerHierarchyRow
89145
containerType={ContainerType.Section}
90146
text={intl.formatMessage(
@@ -94,12 +150,13 @@ const ContainerHierarchy = () => {
94150
count: sections.length,
95151
},
96152
)}
97-
selected={
98-
sections[0].id === containerId
99-
}
153+
showArrow={showSubsections}
154+
selected={selectedSections}
155+
willPublish={selectedSections}
156+
publishMessage={publishMessage(sections)}
100157
/>
101158
)}
102-
{subsections && subsections.length > 0 && (
159+
{showSubsections && (
103160
<ContainerHierarchyRow
104161
containerType={ContainerType.Subsection}
105162
text={intl.formatMessage(
@@ -109,12 +166,13 @@ const ContainerHierarchy = () => {
109166
count: subsections.length,
110167
},
111168
)}
112-
selected={
113-
subsections[0].id === containerId
114-
}
169+
showArrow={showUnits}
170+
selected={selectedSubsections}
171+
willPublish={selectedSubsections || selectedSections}
172+
publishMessage={publishMessage(subsections)}
115173
/>
116174
)}
117-
{units && units.length > 0 && (
175+
{showUnits && (
118176
<ContainerHierarchyRow
119177
containerType={ContainerType.Unit}
120178
text={intl.formatMessage(
@@ -124,12 +182,13 @@ const ContainerHierarchy = () => {
124182
count: units.length,
125183
},
126184
)}
127-
selected={
128-
units[0].id === containerId
129-
}
185+
showArrow={showComponents}
186+
selected={selectedUnits}
187+
willPublish={selectedUnits || selectedSubsections || selectedSections}
188+
publishMessage={publishMessage(units)}
130189
/>
131190
)}
132-
{components && components.length > 0 && (
191+
{showComponents && (
133192
<ContainerHierarchyRow
134193
containerType={ContainerType.Components}
135194
text={intl.formatMessage(
@@ -139,9 +198,10 @@ const ContainerHierarchy = () => {
139198
count: components.length,
140199
},
141200
)}
142-
selected={
143-
components[0].id === containerId
144-
}
201+
showArrow={false}
202+
selected={selectedComponents}
203+
willPublish={selectedComponents || selectedUnits || selectedSubsections || selectedSections}
204+
publishMessage={publishMessage(components)}
145205
/>
146206
)}
147207
</Stack>

0 commit comments

Comments
 (0)