move useI18n to i18n.ts

Since we are using the react-i18n babel loader plugin, useI18n can only
be used once per file. Also we have to remember to add the istanbul
ignore next comment each time we use it. By moving this to a common
file, it can be easily copied and pasted when a new submodule is
created and we don't have to remember the rules when calling it.
This commit is contained in:
David Lechner
2022-05-21 13:57:58 -05:00
parent e7366c8afd
commit a3e55c828a
46 changed files with 256 additions and 168 deletions
+14 -32
View File
@@ -12,7 +12,6 @@ import {
IconName,
useHotkeys,
} from '@blueprintjs/core';
import { I18n, useI18n } from '@shopify/react-i18n';
import React, { useCallback, useMemo, useState } from 'react';
import { useId } from 'react-aria';
import {
@@ -41,7 +40,7 @@ import {
} from './actions';
import DeleteFileAlert from './deleteFileAlert/DeleteFileAlert';
import DuplicateFileDialog from './duplicateFileDialog/DuplicateFileDialog';
import { I18nId } from './i18n';
import { I18nId, useI18n } from './i18n';
import NewFileWizard from './newFileWizard/NewFileWizard';
type ActionButtonProps = {
@@ -91,14 +90,12 @@ type FileTreeItem = TreeItem<FileTreeItemData>;
type ActionButtonGroupProps = {
/** The name of the file (displayed to user) */
item: TreeItem;
/** Translation context. */
i18n: I18n;
};
const FileActionButtonGroup: React.VoidFunctionComponent<ActionButtonGroupProps> = ({
item,
i18n,
}) => {
const i18n = useI18n();
const dispatch = useDispatch();
const environment = useTreeEnvironment();
@@ -145,16 +142,12 @@ const FileActionButtonGroup: React.VoidFunctionComponent<ActionButtonGroupProps>
);
};
type HeaderProps = {
/** Translation context. */
i18n: I18n;
};
const Header: React.VoidFunctionComponent<HeaderProps> = ({ i18n }) => {
const Header: React.VoidFunctionComponent = () => {
const archiveButtonId = useId();
const exportButtonId = useId();
const newButtonId = useId();
const dispatch = useDispatch();
const i18n = useI18n();
return (
<Toolbar
@@ -191,9 +184,10 @@ const Header: React.VoidFunctionComponent<HeaderProps> = ({ i18n }) => {
/**
* Accessibility live descriptors.
* @param i18n Translation context.
*/
function useLiveDescriptors(i18n: I18n): LiveDescriptors {
function useLiveDescriptors(): LiveDescriptors {
const i18n = useI18n();
return useMemo(
() => ({
introduction: `
@@ -301,15 +295,10 @@ const renderTreeContainer: typeof renderers.renderTreeContainer = (props) => {
return <div onKeyDown={handleKeyDown}>{renderers.renderTreeContainer(props)}</div>;
};
type FileTreeProps = {
/** Translation context. */
i18n: I18n;
};
const FileTree: React.VoidFunctionComponent<FileTreeProps> = ({ i18n }) => {
const FileTree: React.VoidFunctionComponent = () => {
const [focusedItem, setFocusedItem] = useState<TreeItemIndex>();
const files = useFileStorageMetadata() ?? [];
const liveDescriptors = useLiveDescriptors(i18n);
const liveDescriptors = useLiveDescriptors();
const rootItemIndex = 'root';
@@ -326,12 +315,7 @@ const FileTree: React.VoidFunctionComponent<FileTreeProps> = ({ i18n }) => {
icon: 'document',
secondaryLabel: (
<TreeItemContext.Consumer>
{(item) => (
<FileActionButtonGroup
item={item}
i18n={i18n}
/>
)}
{(item) => <FileActionButtonGroup item={item} />}
</TreeItemContext.Consumer>
),
},
@@ -348,7 +332,7 @@ const FileTree: React.VoidFunctionComponent<FileTreeProps> = ({ i18n }) => {
},
} as Record<TreeItemIndex, FileTreeItem>,
),
[i18n, files],
[files],
);
const getItemTitle = useCallback((item: FileTreeItem) => item.data.fileName, []);
@@ -361,6 +345,7 @@ const FileTree: React.VoidFunctionComponent<FileTreeProps> = ({ i18n }) => {
);
const dispatch = useDispatch();
const i18n = useI18n();
return (
<ControlledTreeEnvironment<FileTreeItemData>
@@ -388,14 +373,11 @@ const FileTree: React.VoidFunctionComponent<FileTreeProps> = ({ i18n }) => {
};
const Explorer: React.VFC = () => {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useI18n();
return (
<div className="pb-explorer">
<Header i18n={i18n} />
<Header />
<Divider />
<FileTree i18n={i18n} />
<FileTree />
<NewFileWizard />
<DuplicateFileDialog />
<DeleteFileAlert />
+2 -4
View File
@@ -2,10 +2,9 @@
// Copyright (c) 2022 The Pybricks Authors
import { Intent } from '@blueprintjs/core';
import { useI18n } from '@shopify/react-i18n';
import React from 'react';
import { CreateToast } from '../../i18nToaster';
import { I18nId } from './i18n';
import { I18nId, useI18n } from './i18n';
type FileInUseAlertProps = {
fileName: string;
@@ -14,8 +13,7 @@ type FileInUseAlertProps = {
const FileInUseAlert: React.VoidFunctionComponent<FileInUseAlertProps> = ({
fileName,
}) => {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useI18n();
const i18n = useI18n();
return <>{i18n.translate(I18nId.FileInUseMessage, { fileName })}</>;
};
+8
View File
@@ -1,6 +1,14 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
export function useI18n(): I18n {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
export enum I18nId {
FileInUseMessage = 'fileInUse.message',
}
@@ -2,18 +2,16 @@
// Copyright (c) 2022 The Pybricks Authors
import { Alert, Classes, Intent } from '@blueprintjs/core';
import { useI18n } from '@shopify/react-i18n';
import React, { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from '../../reducers';
import { deleteFileAlertDidAccept, deleteFileAlertDidCancel } from './actions';
import { I18nId } from './i18n';
import { I18nId, useI18n } from './i18n';
const DeleteFileAlert: React.VoidFunctionComponent = () => {
const { isOpen, fileName } = useSelector((s) => s.explorer.deleteFileAlert);
const dispatch = useDispatch();
// istanbul ignore next: babel rewrites this line
const [i18n] = useI18n();
const i18n = useI18n();
// a11y: focus primary button when dialog is opened
const handleOpened = useCallback((node: HTMLElement) => {
+8
View File
@@ -1,6 +1,14 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
export function useI18n(): I18n {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
export enum I18nId {
Accept = 'action.accept',
Cancel = 'action.cancel',
@@ -2,7 +2,6 @@
// Copyright (c) 2022 The Pybricks Authors
import { Button, Classes, Dialog } from '@blueprintjs/core';
import { useI18n } from '@shopify/react-i18n';
import React, { useCallback, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useFileStorageMetadata } from '../../fileStorage/hooks';
@@ -13,11 +12,10 @@ import {
import { useSelector } from '../../reducers';
import FileNameFormGroup from '../fileNameFormGroup/FileNameFormGroup';
import { duplicateFileDialogDidAccept, duplicateFileDialogDidCancel } from './actions';
import { I18nId } from './i18n';
import { I18nId, useI18n } from './i18n';
const DuplicateFileDialog: React.VFC = () => {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useI18n();
const i18n = useI18n();
const dispatch = useDispatch();
const isOpen = useSelector((s) => s.explorer.duplicateFileDialog.isOpen);
const oldName = useSelector((s) => s.explorer.duplicateFileDialog.fileName);
+8
View File
@@ -1,6 +1,14 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
export function useI18n(): I18n {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
export enum I18nId {
Title = 'title',
ActionAccept = 'action.accept',
@@ -2,16 +2,13 @@
// Copyright (c) 2022 The Pybricks Authors
import { Classes, FormGroup, InputGroup, Intent, Tag } from '@blueprintjs/core';
import { I18n, useI18n } from '@shopify/react-i18n';
import React from 'react';
import { FileNameValidationResult } from '../../pybricksMicropython/lib';
import { I18nId } from './i18n';
import { I18nId, useI18n } from './i18n';
type FileNameHelpTextProps = {
/** The result of the file name validation. */
validation: FileNameValidationResult;
/** Translation context. */
i18n: I18n;
};
/**
@@ -19,8 +16,9 @@ type FileNameHelpTextProps = {
*/
const FileNameHelpText: React.VoidFunctionComponent<FileNameHelpTextProps> = ({
validation,
i18n,
}) => {
const i18n = useI18n();
switch (validation) {
case FileNameValidationResult.IsOk:
return <>{i18n.translate(I18nId.HelpTextIsOk)}</>;
@@ -78,8 +76,7 @@ const FileNameFormGroup: React.VoidFunctionComponent<FileNameFormGroupProps> = (
inputRef,
onChange,
}) => {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useI18n();
const i18n = useI18n();
const fileNameIntent =
validationResult === FileNameValidationResult.IsOk
@@ -90,7 +87,7 @@ const FileNameFormGroup: React.VoidFunctionComponent<FileNameFormGroupProps> = (
<FormGroup
label={i18n.translate(I18nId.Label)}
intent={fileNameIntent}
subLabel={<FileNameHelpText validation={validationResult} i18n={i18n} />}
subLabel={<FileNameHelpText validation={validationResult} />}
>
<InputGroup
aria-label="File name"
+8
View File
@@ -1,6 +1,14 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
export function useI18n(): I18n {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
export enum I18nId {
Label = 'label',
HelpTextIsOk = 'helpText.isOk',
+8
View File
@@ -3,6 +3,14 @@
//
// Explorer translation keys.
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
export function useI18n(): I18n {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
export enum I18nId {
HeaderToolbarTitle = 'header.toolbar.title',
HeaderToolbarExportAll = 'header.toolbar.exportAll',
+2 -4
View File
@@ -9,7 +9,6 @@ import {
Radio,
RadioGroup,
} from '@blueprintjs/core';
import { useI18n } from '@shopify/react-i18n';
import React, { useCallback, useRef, useState } from 'react';
import { useId } from 'react-aria';
import { useDispatch } from 'react-redux';
@@ -22,14 +21,13 @@ import {
import { useSelector } from '../../reducers';
import FileNameFormGroup from '../fileNameFormGroup/FileNameFormGroup';
import { Hub, newFileWizardDidAccept, newFileWizardDidCancel } from './actions';
import { I18nId } from './i18n';
import { I18nId, useI18n } from './i18n';
// This should be set to the most commonly used hub.
const defaultHub = Hub.Technic;
const NewFileWizard: React.VoidFunctionComponent = () => {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useI18n();
const i18n = useI18n();
const dispatch = useDispatch();
const isOpen = useSelector((s) => s.explorer.newFileWizard.isOpen);
+8
View File
@@ -1,6 +1,14 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
export function useI18n(): I18n {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
export enum I18nId {
Title = 'title',
SmartHubLabel = 'smartHub.label',
@@ -2,7 +2,6 @@
// Copyright (c) 2022 The Pybricks Authors
import { Button, Classes, Dialog } from '@blueprintjs/core';
import { useI18n } from '@shopify/react-i18n';
import React, { useCallback, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useFileStorageMetadata } from '../../fileStorage/hooks';
@@ -13,11 +12,11 @@ import {
import { useSelector } from '../../reducers';
import FileNameFormGroup from '../fileNameFormGroup/FileNameFormGroup';
import { renameFileDialogDidAccept, renameFileDialogDidCancel } from './actions';
import { useI18n } from './i18n';
import { I18nId } from './i18n';
const RenameFileDialog: React.VFC = () => {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useI18n();
const i18n = useI18n();
const dispatch = useDispatch();
const isOpen = useSelector((s) => s.explorer.renameFileDialog.isOpen);
const oldName = useSelector((s) => s.explorer.renameFileDialog.fileName);
+8
View File
@@ -1,6 +1,14 @@
// SPDX-License-Identifier: MIT
// Copyright (c) 2022 The Pybricks Authors
import { I18n, useI18n as useShopifyI18n } from '@shopify/react-i18n';
export function useI18n(): I18n {
// istanbul ignore next: babel-loader rewrites this line
const [i18n] = useShopifyI18n();
return i18n;
}
export enum I18nId {
Title = 'title',
ActionRename = 'action.rename',