@@ -5,9 +5,10 @@ import {
55}  from  '@gouvfr-lasuite/ui-kit' ; 
66import  {  useRouter  }  from  'next/navigation' ; 
77import  {  useState  }  from  'react' ; 
8+ import  {  useTranslation  }  from  'react-i18next' ; 
89import  {  css  }  from  'styled-components' ; 
910
10- import  {  Box ,  Icon ,  Text  }  from  '@/components' ; 
11+ import  {  Box ,  BoxButton ,   Icon ,  Text  }  from  '@/components' ; 
1112import  {  useCunninghamTheme  }  from  '@/cunningham' ; 
1213import  { 
1314  Doc , 
@@ -20,6 +21,7 @@ import { useResponsiveStore } from '@/stores';
2021
2122import  SubPageIcon  from  './../assets/sub-page-logo.svg' ; 
2223import  {  DocTreeItemActions  }  from  './DocTreeItemActions' ; 
24+ import  {  useKeyboardActivation  }  from  './hooks/useKeyboardActivation' ; 
2325
2426const  ItemTextCss  =  css ` 
2527  overflow :  hidden; 
@@ -38,14 +40,23 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
3840  const  {  node }  =  props ; 
3941  const  {  spacingsTokens }  =  useCunninghamTheme ( ) ; 
4042  const  {  isDesktop }  =  useResponsiveStore ( ) ; 
41-   const  [ actionsOpen ,  setActionsOpen ]  =  useState ( false ) ; 
43+   const  {  t }  =  useTranslation ( ) ; 
44+ 
45+   const  [ menuOpen ,  setMenuOpen ]  =  useState ( false ) ; 
46+   const  isSelectedNow  =  treeContext ?. treeData . selectedNode ?. id  ===  doc . id ; 
47+   const  isActive  =  node . isFocused  ||  menuOpen  ||  isSelectedNow ; 
4248
4349  const  router  =  useRouter ( ) ; 
4450  const  {  togglePanel }  =  useLeftPanelStore ( ) ; 
4551
4652  const  {  emoji,  titleWithoutEmoji }  =  getEmojiAndTitle ( doc . title  ||  '' ) ; 
4753  const  displayTitle  =  titleWithoutEmoji  ||  untitledDocument ; 
4854
55+   const  handleActivate  =  ( )  =>  { 
56+     treeContext ?. treeData . setSelectedNode ( doc ) ; 
57+     router . push ( `/docs/${ doc . id }  ) ; 
58+   } ; 
59+ 
4960  const  afterCreate  =  ( createdDoc : Doc )  =>  { 
5061    const  actualChildren  =  node . data . children  ??  [ ] ; 
5162
@@ -76,62 +87,87 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
7687    } 
7788  } ; 
7889
90+   useKeyboardActivation ( 
91+     [ 'Enter' ,  ' ' ] , 
92+     isActive  &&  ! menuOpen , 
93+     handleActivate , 
94+     true , 
95+   ) ; 
96+ 
97+   // prepare the text for the screen reader 
98+   const  docTitle  =  doc . title  ||  untitledDocument ; 
99+   const  hasChildren  =  ( doc . children ?. length  ||  0 )  >  0 ; 
100+   const  isExpanded  =  node . isOpen ; 
101+   const  isSelected  =  isSelectedNow ; 
102+ 
103+   const  ariaLabel  =  docTitle ; 
104+ 
79105  return  ( 
80106    < Box 
81107      className = "--docs-sub-page-item" 
82108      draggable = { doc . abilities . move  &&  isDesktop } 
83109      $position = "relative" 
110+       role = "treeitem" 
111+       aria-label = { ariaLabel } 
112+       aria-selected = { isSelected } 
113+       aria-expanded = { hasChildren  ? isExpanded  : undefined } 
84114      $css = { css ` 
85-         background-color : ${ actionsOpen  
86-           ? 'var(--c--theme--colors--greyscale-100)'  
87-           : 'var(--c--theme--colors--greyscale-000)' }  ;
115+         /* Ensure the outline (handled by TreeView) matches the visual area */  
116+         .c__tree-view--node  { 
117+           padding : ${ spacingsTokens [ '3xs' ] }  
118+           border-radius :  4px  ; 
119+         } 
88120
89121        .light-doc-item-actions  { 
90-           display : ${ actionsOpen  ||  ! isDesktop  ? 'flex'  : 'none' }  
122+           display :  flex; 
123+           opacity : ${ isActive  ||  ! isDesktop  ? 1  : 0 }  
91124          position :  absolute; 
92125          right :  0 ; 
93-           background :   ${ isDesktop  
94-             ?  'var(--c--theme--colors--greyscale- 100)'  
95-             :  'var(--c--theme--colors--greyscale-000)' }  ;
126+           top :   0 ;  
127+           height :   100%  ;  
128+           z-index :   10 ; 
96129        } 
97130
98-         .c__tree-view--node .isSelected  { 
99-           .light-doc-item-actions  { 
100-             background :  var (--c--theme--colors--greyscale-100 ); 
101-           } 
102-         } 
103- 
104-         & : hover  { 
131+         .c__tree-view--node : hover ,  
132+         .c__tree-view--node .isFocused  { 
105133          background-color :  var (--c--theme--colors--greyscale-100 ); 
106-           border-radius :  4px  ; 
107134
108135          .light-doc-item-actions  { 
109136            display :  flex; 
110-             background :  var (--c--theme--colors--greyscale-100 ); 
137+             opacity :  1 ; 
138+             visibility :  visible; 
139+             /* background: var(--c--theme--colors--greyscale-100); */  
111140          } 
112141        } 
113142
114143        .row .preview  &  { 
115144          background-color :  inherit; 
116145        } 
146+ 
147+         /* Ensure actions are visible when hovering the whole item container */  
148+         & : hover  { 
149+           .light-doc-item-actions  { 
150+             display :  flex; 
151+             opacity :  1 ; 
152+             visibility :  visible; 
153+           } 
154+         } 
117155      ` } 
118156    > 
119-       < TreeViewItem 
120-         { ...props } 
121-         onClick = { ( )  =>  { 
122-           treeContext ?. treeData . setSelectedNode ( props . node . data . value  as  Doc ) ; 
123-           router . push ( `/docs/${ props . node . data . value . id }  ) ; 
124-         } } 
125-       > 
126-         < Box 
127-           data-testid = { `doc-sub-page-item-${ props . node . data . value . id }  } 
157+       < TreeViewItem  { ...props }  onClick = { handleActivate } > 
158+         < BoxButton 
159+           onClick = { ( e )  =>  { 
160+             e . stopPropagation ( ) ; 
161+             handleActivate ( ) ; 
162+           } } 
163+           tabIndex = { - 1 } 
128164          $width = "100%" 
129165          $direction = "row" 
130166          $gap = { spacingsTokens [ 'xs' ] } 
131-           role = "button" 
132-           tabIndex = { 0 } 
133167          $align = "center" 
134168          $minHeight = "24px" 
169+           data-testid = { `doc-sub-page-item-${ doc . id }  } 
170+           aria-label = { `${ t ( 'Open document' ) } ${ docTitle }  } 
135171        > 
136172          < Box  $width = "16px"  $height = "16px" > 
137173            < DocIcon  emoji = { emoji }  defaultIcon = { < SubPageIcon  /> }  $size = "sm"  /> 
@@ -157,25 +193,28 @@ export const DocSubPageItem = (props: TreeViewNodeProps<Doc>) => {
157193                iconName = "group" 
158194                $size = "16px" 
159195                $variation = "400" 
196+                 aria-hidden = "true" 
160197              /> 
161198            ) } 
162199          </ Box > 
163- 
164-           < Box 
165-             $direction = "row" 
166-             $align = "center" 
167-             className = "light-doc-item-actions" 
168-           > 
169-             < DocTreeItemActions 
170-               doc = { doc } 
171-               isOpen = { actionsOpen } 
172-               onOpenChange = { setActionsOpen } 
173-               parentId = { node . data . parentKey } 
174-               onCreateSuccess = { afterCreate } 
175-             /> 
176-           </ Box > 
177-         </ Box > 
200+         </ BoxButton > 
178201      </ TreeViewItem > 
202+ 
203+       < Box 
204+         $direction = "row" 
205+         $align = "center" 
206+         className = "light-doc-item-actions" 
207+         role = "toolbar" 
208+         aria-label = { `${ t ( 'Actions for' ) } ${ docTitle }  } 
209+       > 
210+         < DocTreeItemActions 
211+           doc = { doc } 
212+           isOpen = { menuOpen } 
213+           onOpenChange = { setMenuOpen } 
214+           parentId = { node . data . parentKey } 
215+           onCreateSuccess = { afterCreate } 
216+         /> 
217+       </ Box > 
179218    </ Box > 
180219  ) ; 
181220} ; 
0 commit comments