1717 *
1818 */
1919
20- import { ChangeEvent , CSSProperties , ReactNode } from 'react' ;
20+ import { ChangeEvent , CSSProperties , ReactNode , useEffect , useId , useRef } from 'react' ;
2121
2222import { BlurHighIcon , BlurLowIcon , Checkbox , CheckboxLabel , CircleIcon } from '@wireapp/react-ui-kit' ;
2323
@@ -31,6 +31,7 @@ import {
3131 backgroundSettingsScrollableContentStyles ,
3232 backgroundSettingsTitleStyles ,
3333 backgroundSettingsWrapperStyles ,
34+ closeButtonStyles ,
3435 sectionLabelStyles ,
3536 tileButtonStyles ,
3637 tileGridStyles ,
@@ -60,10 +61,26 @@ const isEffectSelected = (selected: BackgroundEffectSelection, candidate: Backgr
6061 return true ;
6162} ;
6263
64+ const getBackgroundEffectLabel = ( effect : BackgroundEffectSelection , backgrounds : BuiltinBackground [ ] ) : string => {
65+ switch ( effect . type ) {
66+ case 'none' :
67+ return t ( 'videoCallBackgroundNoEffect' ) ;
68+ case 'blur' :
69+ return effect . level === 'low' ? t ( 'videoCallBackgroundBlurLow' ) : t ( 'videoCallBackgroundBlurHigh' ) ;
70+ case 'virtual' : {
71+ const background = backgrounds . find ( ( { id} ) => id === effect . backgroundId ) ;
72+ return background ? t ( background . labelKey ) : t ( 'videoCallBackgroundVirtual' ) ;
73+ }
74+ default :
75+ return t ( 'videoCallBackgroundEffectsLabel' ) ;
76+ }
77+ } ;
78+
6379interface BackgroundTileProps {
6480 effect : BackgroundEffectSelection ;
6581 selectedEffect : BackgroundEffectSelection ;
6682 onSelectEffect : ( effect : BackgroundEffectSelection ) => void ;
83+ ariaLabel : string ;
6784 previewContent ?: ReactNode ;
6885 previewStyle ?: CSSProperties ;
6986}
@@ -72,6 +89,7 @@ const BackgroundTile = ({
7289 effect,
7390 selectedEffect,
7491 onSelectEffect,
92+ ariaLabel,
7593 previewContent,
7694 previewStyle,
7795} : BackgroundTileProps ) => {
@@ -81,7 +99,9 @@ const BackgroundTile = ({
8199 type = "button"
82100 css = { tileButtonStyles }
83101 data-selected = { selected }
84- aria-pressed = { selected }
102+ role = "radio"
103+ aria-checked = { selected }
104+ aria-label = { ariaLabel }
85105 onClick = { ( ) => onSelectEffect ( effect ) }
86106 >
87107 < div css = { tilePreviewStyles } style = { previewStyle } className = "bg-tile__preview" >
@@ -99,25 +119,54 @@ export const VideoBackgroundSettings = ({
99119 onEnableHighQualityBlur,
100120 onClose,
101121} : VideoBackgroundSettingsProps ) => {
122+ const titleId = useId ( ) ;
123+ const blurSectionId = useId ( ) ;
124+ const virtualSectionId = useId ( ) ;
125+ const closeButtonRef = useRef < HTMLButtonElement > ( null ) ;
126+
127+ useEffect ( ( ) => {
128+ closeButtonRef . current ?. focus ( ) ;
129+ } , [ ] ) ;
130+
102131 const handleEnableHighQualityBlur = ( event : ChangeEvent < HTMLInputElement > ) => {
103132 onEnableHighQualityBlur ( event ) ;
104133 } ;
105134
135+ const noneEffect : BackgroundEffectSelection = { type : 'none' } ;
136+ const lowBlurEffect : BackgroundEffectSelection = { type : 'blur' , level : 'low' } ;
137+ const highBlurEffect : BackgroundEffectSelection = { type : 'blur' , level : 'high' } ;
138+
106139 return (
107- < div css = { backgroundSettingsWrapperStyles } data-uie-name = "video-background-settings" >
140+ < div
141+ css = { backgroundSettingsWrapperStyles }
142+ data-uie-name = "video-background-settings"
143+ role = "region"
144+ aria-labelledby = { titleId }
145+ >
108146 < div css = { backgroundSettingsHeaderStyles } >
109- < span css = { backgroundSettingsTitleStyles } > { t ( 'videoCallBackgroundEffectsLabel' ) } </ span >
110- < button type = "button" className = "icon-button" onClick = { onClose } title = { t ( 'modalCloseButton' ) } >
147+ < h2 id = { titleId } css = { backgroundSettingsTitleStyles } >
148+ { t ( 'videoCallBackgroundEffectsLabel' ) }
149+ </ h2 >
150+ < button
151+ ref = { closeButtonRef }
152+ type = "button"
153+ css = { closeButtonStyles }
154+ className = "icon-button"
155+ onClick = { onClose }
156+ aria-label = { t ( 'modalCloseButton' ) }
157+ title = { t ( 'modalCloseButton' ) }
158+ >
111159 < Icon . CloseIcon width = { 12 } height = { 12 } />
112160 </ button >
113161 </ div >
114162
115163 < FadingScrollbar css = { backgroundSettingsScrollableContentStyles } >
116164 { /* No background effect — full-width tile */ }
117165 < BackgroundTile
118- effect = { { type : 'none' } }
166+ effect = { noneEffect }
119167 selectedEffect = { selectedEffect }
120168 onSelectEffect = { onSelectEffect }
169+ ariaLabel = { getBackgroundEffectLabel ( noneEffect , backgrounds ) }
121170 previewContent = {
122171 < div css = { tilePreviewContentStyles } >
123172 < CircleIcon />
@@ -128,12 +177,15 @@ export const VideoBackgroundSettings = ({
128177
129178 { /* Blur section */ }
130179 < div >
131- < div css = { sectionLabelStyles } > { t ( 'videoCallBackgroundBlurSectionLabel' ) } </ div >
132- < div css = { tileGridStyles } >
180+ < h3 id = { blurSectionId } css = { sectionLabelStyles } >
181+ { t ( 'videoCallBackgroundBlurSectionLabel' ) }
182+ </ h3 >
183+ < div css = { tileGridStyles } role = "group" aria-labelledby = { blurSectionId } >
133184 < BackgroundTile
134- effect = { { type : 'blur' , level : 'low' } }
185+ effect = { lowBlurEffect }
135186 selectedEffect = { selectedEffect }
136187 onSelectEffect = { onSelectEffect }
188+ ariaLabel = { getBackgroundEffectLabel ( lowBlurEffect , backgrounds ) }
137189 previewContent = {
138190 < div css = { tilePreviewContentStyles } >
139191 < BlurLowIcon />
@@ -142,9 +194,10 @@ export const VideoBackgroundSettings = ({
142194 }
143195 />
144196 < BackgroundTile
145- effect = { { type : 'blur' , level : 'high' } }
197+ effect = { highBlurEffect }
146198 selectedEffect = { selectedEffect }
147199 onSelectEffect = { onSelectEffect }
200+ ariaLabel = { getBackgroundEffectLabel ( highBlurEffect , backgrounds ) }
148201 previewContent = {
149202 < div css = { tilePreviewContentStyles } >
150203 < BlurHighIcon />
@@ -171,19 +224,26 @@ export const VideoBackgroundSettings = ({
171224
172225 { /* Virtual backgrounds section */ }
173226 < div >
174- < div css = { sectionLabelStyles } > { t ( 'videoCallBackgroundVirtualSectionLabel' ) } </ div >
175- < div css = { tileGridStyles } >
176- { backgrounds . map ( background => (
177- < BackgroundTile
178- key = { background . id }
179- effect = { { type : 'virtual' , backgroundId : background . id } }
180- selectedEffect = { selectedEffect }
181- onSelectEffect = { onSelectEffect }
182- previewStyle = { {
183- backgroundImage : `url(${ background . imageUrl } ), ${ background . previewGradient } ` ,
184- } }
185- />
186- ) ) }
227+ < h3 id = { virtualSectionId } css = { sectionLabelStyles } >
228+ { t ( 'videoCallBackgroundVirtualSectionLabel' ) }
229+ </ h3 >
230+ < div css = { tileGridStyles } role = "group" aria-labelledby = { virtualSectionId } >
231+ { backgrounds . map ( background => {
232+ const virtualEffect : BackgroundEffectSelection = { type : 'virtual' , backgroundId : background . id } ;
233+
234+ return (
235+ < BackgroundTile
236+ key = { background . id }
237+ effect = { virtualEffect }
238+ selectedEffect = { selectedEffect }
239+ onSelectEffect = { onSelectEffect }
240+ ariaLabel = { getBackgroundEffectLabel ( virtualEffect , backgrounds ) }
241+ previewStyle = { {
242+ backgroundImage : `url(${ background . imageUrl } ), ${ background . previewGradient } ` ,
243+ } }
244+ />
245+ ) ;
246+ } ) }
187247 </ div >
188248 </ div >
189249 </ FadingScrollbar >
0 commit comments