@@ -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