Skip to content

Commit 1576dd9

Browse files
authored
Merge pull request #549 from sbates-idrc/GH-547
Maintain focus after changing language
2 parents fb5e454 + 68a226d commit 1576dd9

2 files changed

Lines changed: 40 additions & 11 deletions

File tree

src/App.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,15 @@ export class App extends React.Component<AppProps, AppState> {
145145
customBackgroundSerializer: CustomBackgroundSerializer;
146146
speedLookUp: Array<number>;
147147
pushStateTimeoutID: ?TimeoutID;
148+
languageSelectorRef: { current: any };
148149
programBlockEditorRef: { current: any };
149150
sequenceInProgress: Array<KeyboardEvent>;
150151
announcementBuilder: AnnouncementBuilder;
151152
characterDescriptionBuilder: CharacterDescriptionBuilder;
152153
designModeCursorDescriptionBuilder: DesignModeCursorDescriptionBuilder;
153154
programChangeController: ProgramChangeController;
154155
defaultWorld: WorldName;
156+
focusLanguageSelector: boolean;
155157

156158
constructor(props: any) {
157159
super(props);
@@ -180,6 +182,8 @@ export class App extends React.Component<AppProps, AppState> {
180182

181183
this.defaultWorld = 'Sketchpad';
182184

185+
this.focusLanguageSelector = false;
186+
183187
// Initialize startingX, startingY, and startingDirection to the world starting position
184188
const startingX = getWorldProperties(this.defaultWorld).startingX;
185189
const startingY = getWorldProperties(this.defaultWorld).startingY;
@@ -247,6 +251,8 @@ export class App extends React.Component<AppProps, AppState> {
247251
this.programChangeController = new ProgramChangeController(this,
248252
this.audioManager);
249253

254+
this.languageSelectorRef = React.createRef();
255+
250256
this.programBlockEditorRef = React.createRef();
251257

252258
const actionsHandler = new ActionsHandler(this, this.audioManager,
@@ -333,6 +339,15 @@ export class App extends React.Component<AppProps, AppState> {
333339

334340
// Handlers
335341

342+
handleChangeLanguage = (language: LanguageTag) => {
343+
// Changing the language will cause the App to update and for focus to
344+
// be lost. Set this.focusLanguageSelector to true to indicate that we
345+
// want to set focus back to the language selector after the update.
346+
this.focusLanguageSelector = true;
347+
// Call the provided handler
348+
this.props.onChangeLanguage(language);
349+
};
350+
336351
handleProgramSequenceChange = (programSequence: ProgramSequence) => {
337352
this.setState({
338353
programSequence: programSequence
@@ -1278,7 +1293,8 @@ export class App extends React.Component<AppProps, AppState> {
12781293
<div className='App__PrivacyButtonLanguageSelectorRow'>
12791294
<LanguageSelector
12801295
value={this.props.language}
1281-
onChange={this.props.onChangeLanguage}
1296+
onChange={this.handleChangeLanguage}
1297+
ref={this.languageSelectorRef}
12821298
/>
12831299
<button
12841300
aria-label={this.props.intl.formatMessage({id: 'App.privacyModalToggle.ariaLabel'})}
@@ -1881,7 +1897,7 @@ export class App extends React.Component<AppProps, AppState> {
18811897
document.addEventListener('keydown', this.handleDocumentKeyDown);
18821898
}
18831899

1884-
componentDidUpdate(prevProps: {}, prevState: AppState) {
1900+
componentDidUpdate(prevProps: AppProps, prevState: AppState) {
18851901
if (this.state.programSequence !== prevState.programSequence
18861902
|| this.state.characterState !== prevState.characterState
18871903
|| this.state.settings.theme !== prevState.settings.theme
@@ -1981,6 +1997,16 @@ export class App extends React.Component<AppProps, AppState> {
19811997
);
19821998
}
19831999
}
2000+
2001+
// If the language has been changed and this.focusLanguageSelector is
2002+
// set, then set focus to the language selector
2003+
if (this.props.language !== prevProps.language
2004+
&& this.focusLanguageSelector) {
2005+
if (this.languageSelectorRef.current) {
2006+
this.languageSelectorRef.current.focus();
2007+
}
2008+
this.focusLanguageSelector = false;
2009+
}
19842010
}
19852011

19862012
componentWillUnmount() {

src/LanguageSelector.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,26 @@ type LanguageSelectorProps = {
99
onChange: (value: LanguageTag) => void
1010
};
1111

12-
export default class LanguageSelector extends React.PureComponent<LanguageSelectorProps, {}> {
13-
handleClick = () => {
14-
this.props.onChange(this.props.value === 'en' ? 'fr' : 'en');
15-
};
12+
const LanguageSelector = React.forwardRef<LanguageSelectorProps, HTMLButtonElement>(
13+
(props, ref) => {
14+
const handleClick = () => {
15+
props.onChange(props.value === 'en' ? 'fr' : 'en');
16+
};
1617

17-
render() {
18-
const label = this.props.value === 'en' ? 'Français' : 'English';
19-
const labelLang = this.props.value === 'en' ? 'fr' : 'en';
18+
const label = props.value === 'en' ? 'Français' : 'English';
19+
const labelLang = props.value === 'en' ? 'fr' : 'en';
2020

2121
return (
2222
<button
2323
className='LanguageSelector'
2424
lang={labelLang}
25-
onClick={this.handleClick}
25+
onClick={handleClick}
26+
ref={ref}
2627
>
2728
{label}
2829
</button>
2930
);
3031
}
31-
};
32+
);
33+
34+
export default LanguageSelector;

0 commit comments

Comments
 (0)