@@ -5,29 +5,33 @@ import ListSeparator from '@ui5/webcomponents/dist/types/ListSeparator.js';
5
5
import TitleLevel from '@ui5/webcomponents/dist/types/TitleLevel.js' ;
6
6
import WrappingType from '@ui5/webcomponents/dist/types/WrappingType.js' ;
7
7
import ValueState from '@ui5/webcomponents-base/dist/types/ValueState.js' ;
8
+ import announce from '@ui5/webcomponents-base/dist/util/InvisibleMessage.js' ;
8
9
import iconSlimArrowLeft from '@ui5/webcomponents-icons/dist/slim-arrow-left.js' ;
10
+ import type { Ui5DomRef } from '@ui5/webcomponents-react-base' ;
9
11
import { useI18nBundle , useStylesheet , useSyncRef } from '@ui5/webcomponents-react-base' ;
10
12
import { clsx } from 'clsx' ;
11
13
import type { ReactElement , ReactNode } from 'react' ;
12
- import { Children , forwardRef , Fragment , isValidElement , useCallback , useEffect , useState } from 'react' ;
14
+ import { useRef , Children , forwardRef , Fragment , isValidElement , useCallback , useEffect , useState } from 'react' ;
13
15
import { FlexBoxDirection } from '../../enums/index.js' ;
14
- import { ALL , LIST_NO_DATA } from '../../i18n/i18n-defaults.js' ;
16
+ import { ALL , LIST_NO_DATA , NAVIGATE_BACK , MESSAGE_DETAILS , MESSAGE_TYPES } from '../../i18n/i18n-defaults.js' ;
17
+ import type { SelectedMessage } from '../../internal/MessageViewContext.js' ;
15
18
import { MessageViewContext } from '../../internal/MessageViewContext.js' ;
16
19
import type { CommonProps } from '../../types/index.js' ;
17
20
import { Bar } from '../../webComponents/Bar/index.js' ;
21
+ import type { ButtonDomRef } from '../../webComponents/Button/index.js' ;
18
22
import { Button } from '../../webComponents/Button/index.js' ;
19
23
import { Icon } from '../../webComponents/Icon/index.js' ;
20
- import type { ListPropTypes } from '../../webComponents/List/index.js' ;
24
+ import type { ListDomRef , ListPropTypes } from '../../webComponents/List/index.js' ;
21
25
import { List } from '../../webComponents/List/index.js' ;
22
26
import { ListItemGroup } from '../../webComponents/ListItemGroup/index.js' ;
23
- import type { SegmentedButtonPropTypes } from '../../webComponents/SegmentedButton/index.js' ;
24
27
import { SegmentedButton } from '../../webComponents/SegmentedButton/index.js' ;
28
+ import type { SegmentedButtonPropTypes } from '../../webComponents/SegmentedButton/index.js' ;
25
29
import { SegmentedButtonItem } from '../../webComponents/SegmentedButtonItem/index.js' ;
26
30
import { Title } from '../../webComponents/Title/index.js' ;
27
31
import { FlexBox } from '../FlexBox/index.js' ;
28
32
import type { MessageItemPropTypes } from './MessageItem.js' ;
29
33
import { classNames , styleData } from './MessageView.module.css.js' ;
30
- import { getIconNameForType } from './utils.js' ;
34
+ import { getIconNameForType , getValueStateMap } from './utils.js' ;
31
35
32
36
export interface MessageViewDomRef extends HTMLDivElement {
33
37
/**
@@ -107,6 +111,10 @@ export const resolveMessageGroups = (children: ReactElement<MessageItemPropTypes
107
111
*/
108
112
const MessageView = forwardRef < MessageViewDomRef , MessageViewPropTypes > ( ( props , ref ) => {
109
113
const { children, groupItems, showDetailsPageHeader, className, onItemSelect, ...rest } = props ;
114
+ const navBtnRef = useRef < ButtonDomRef > ( null ) ;
115
+ const listRef = useRef < ListDomRef > ( null ) ;
116
+ const transitionTrigger = useRef < 'btn' | 'list' | null > ( null ) ;
117
+ const prevSelectedMessage = useRef < SelectedMessage > ( null ) ;
110
118
111
119
useStylesheet ( styleData , MessageView . displayName ) ;
112
120
@@ -115,7 +123,7 @@ const MessageView = forwardRef<MessageViewDomRef, MessageViewPropTypes>((props,
115
123
const i18nBundle = useI18nBundle ( '@ui5/webcomponents-react' ) ;
116
124
117
125
const [ listFilter , setListFilter ] = useState < ValueState | 'All' > ( 'All' ) ;
118
- const [ selectedMessage , setSelectedMessage ] = useState < MessageItemPropTypes > ( null ) ;
126
+ const [ selectedMessage , setSelectedMessage ] = useState < SelectedMessage > ( null ) ;
119
127
120
128
const childrenArray = Children . toArray ( children ) ;
121
129
const messageTypes = resolveMessageTypes ( childrenArray as ReactElement < MessageItemPropTypes > [ ] ) ;
@@ -138,8 +146,10 @@ const MessageView = forwardRef<MessageViewDomRef, MessageViewPropTypes>((props,
138
146
const groupedMessages = resolveMessageGroups ( filteredChildren as ReactElement < MessageItemPropTypes > [ ] ) ;
139
147
140
148
const navigateBack = useCallback ( ( ) => {
149
+ transitionTrigger . current = 'btn' ;
150
+ prevSelectedMessage . current = selectedMessage ;
141
151
setSelectedMessage ( null ) ;
142
- } , [ setSelectedMessage ] ) ;
152
+ } , [ setSelectedMessage , selectedMessage ] ) ;
143
153
144
154
useEffect ( ( ) => {
145
155
if ( internalRef . current ) {
@@ -151,10 +161,37 @@ const MessageView = forwardRef<MessageViewDomRef, MessageViewPropTypes>((props,
151
161
setListFilter ( e . detail . selectedItems . at ( 0 ) . dataset . key as never ) ;
152
162
} ;
153
163
154
- const outerClasses = clsx ( classNames . container , className , selectedMessage && classNames . showDetails ) ;
164
+ const handleTransitionEnd : MessageViewPropTypes [ 'onTransitionEnd' ] = ( e ) => {
165
+ if ( typeof props ?. onTransitionEnd === 'function' ) {
166
+ props . onTransitionEnd ( e ) ;
167
+ }
168
+ if ( showDetailsPageHeader && transitionTrigger . current === 'list' ) {
169
+ requestAnimationFrame ( ( ) => {
170
+ void navBtnRef . current ?. focus ( ) ;
171
+ } ) ;
172
+ setTimeout ( ( ) => {
173
+ announce ( i18nBundle . getText ( MESSAGE_DETAILS ) , 'Polite' ) ;
174
+ } , 300 ) ;
175
+ }
176
+ if ( transitionTrigger . current === 'btn' ) {
177
+ requestAnimationFrame ( ( ) => {
178
+ const selectedItem = listRef . current . querySelector < Ui5DomRef > (
179
+ `[data-title="${ CSS . escape ( prevSelectedMessage . current . titleTextStr ) } "]`
180
+ ) ;
181
+ void selectedItem . focus ( ) ;
182
+ } ) ;
183
+ }
184
+ transitionTrigger . current = null ;
185
+ } ;
155
186
187
+ const handleListItemClick : ListPropTypes [ 'onItemClick' ] = ( e ) => {
188
+ transitionTrigger . current = 'list' ;
189
+ onItemSelect ( e ) ;
190
+ } ;
191
+
192
+ const outerClasses = clsx ( classNames . container , className , selectedMessage && classNames . showDetails ) ;
156
193
return (
157
- < div ref = { componentRef } { ...rest } className = { outerClasses } >
194
+ < div ref = { componentRef } { ...rest } className = { outerClasses } onTransitionEnd = { handleTransitionEnd } >
158
195
< MessageViewContext . Provider
159
196
value = { {
160
197
selectMessage : setSelectedMessage
@@ -166,7 +203,10 @@ const MessageView = forwardRef<MessageViewDomRef, MessageViewPropTypes>((props,
166
203
{ filledTypes > 1 && (
167
204
< Bar
168
205
startContent = {
169
- < SegmentedButton onSelectionChange = { handleListFilterChange } >
206
+ < SegmentedButton
207
+ onSelectionChange = { handleListFilterChange }
208
+ accessibleName = { i18nBundle . getText ( MESSAGE_TYPES ) }
209
+ >
170
210
< SegmentedButtonItem data-key = "All" selected = { listFilter === 'All' } >
171
211
{ i18nBundle . getText ( ALL ) }
172
212
</ SegmentedButtonItem >
@@ -182,6 +222,8 @@ const MessageView = forwardRef<MessageViewDomRef, MessageViewPropTypes>((props,
182
222
selected = { listFilter === valueState }
183
223
icon = { getIconNameForType ( valueState ) }
184
224
className = { classNames . button }
225
+ tooltip = { getValueStateMap ( i18nBundle ) [ valueState ] }
226
+ accessibleName = { getValueStateMap ( i18nBundle ) [ valueState ] }
185
227
>
186
228
{ count }
187
229
</ SegmentedButtonItem >
@@ -192,7 +234,8 @@ const MessageView = forwardRef<MessageViewDomRef, MessageViewPropTypes>((props,
192
234
/>
193
235
) }
194
236
< List
195
- onItemClick = { onItemSelect }
237
+ ref = { listRef }
238
+ onItemClick = { handleListItemClick }
196
239
noDataText = { i18nBundle . getText ( LIST_NO_DATA ) }
197
240
separators = { ListSeparator . Inner }
198
241
>
@@ -212,13 +255,21 @@ const MessageView = forwardRef<MessageViewDomRef, MessageViewPropTypes>((props,
212
255
</ >
213
256
) }
214
257
</ div >
215
- < div className = { classNames . detailsContainer } >
258
+ < div className = { classNames . detailsContainer } data-component-name = "MessageViewDetailsContainer" >
216
259
{ childrenArray . length > 0 ? (
217
260
< >
218
261
{ showDetailsPageHeader && selectedMessage && (
219
262
< Bar
220
263
startContent = {
221
- < Button design = { ButtonDesign . Transparent } icon = { iconSlimArrowLeft } onClick = { navigateBack } />
264
+ < Button
265
+ ref = { navBtnRef }
266
+ design = { ButtonDesign . Transparent }
267
+ icon = { iconSlimArrowLeft }
268
+ onClick = { navigateBack }
269
+ tooltip = { i18nBundle . getText ( NAVIGATE_BACK ) }
270
+ accessibleName = { i18nBundle . getText ( NAVIGATE_BACK ) }
271
+ data-component-name = "MessageViewDetailsNavBackBtn"
272
+ />
222
273
}
223
274
/>
224
275
) }
0 commit comments