1- import { useIntl } from '@edx/frontend-platform/i18n' ;
1+ import type { MessageDescriptor } from 'react-intl' ;
2+ import { FormattedMessage , useIntl } from '@edx/frontend-platform/i18n' ;
23import { Container , Icon , Stack } from '@openedx/paragon' ;
3- import { ArrowDownward } from '@openedx/paragon/icons' ;
4+ import { ArrowDownward , Check , Description } from '@openedx/paragon/icons' ;
45import classNames from 'classnames' ;
56import { getItemIcon } from '@src/generic/block-type-utils' ;
67import Loading from '@src/generic/Loading' ;
7- import NotFoundAlert from '@src/generic/NotFoundAlert' ;
88import { ContainerType } from '@src/generic/key-utils' ;
9+ import type { ContainerHierarchyMember } from '../data/api' ;
910import { useContainerHierarchy } from '../data/apiHooks' ;
1011import { useSidebarContext } from '../common/context/SidebarContext' ;
1112import 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