mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-12 01:23:52 +00:00
use i18n for buttons
This commit is contained in:
committed by
David Lechner
parent
24acab901c
commit
0d30e113da
@@ -1,17 +1,20 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Image from 'react-bootstrap/Image';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import { TooltipId } from './button';
|
||||
import en from './button.en.json';
|
||||
|
||||
export interface ActionButtonProps {
|
||||
/** A unique id for each instance. */
|
||||
readonly id: string;
|
||||
/** Tooltip text that appears when hovering over the button. */
|
||||
readonly tooltip: string;
|
||||
readonly tooltip: TooltipId;
|
||||
/** Icon shown on the button. */
|
||||
readonly icon: string;
|
||||
/** When true or undefined, the button is enabled. */
|
||||
@@ -20,14 +23,16 @@ export interface ActionButtonProps {
|
||||
readonly onAction: () => void;
|
||||
}
|
||||
|
||||
class ActionButton extends React.Component<ActionButtonProps> {
|
||||
type Props = ActionButtonProps & WithI18nProps;
|
||||
|
||||
class ActionButton extends React.Component<Props> {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<OverlayTrigger
|
||||
placement="bottom"
|
||||
overlay={
|
||||
<Tooltip id={`${this.props.id}-tooltip`}>
|
||||
{this.props.tooltip}.
|
||||
{this.props.i18n.translate(this.props.tooltip)}.
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
@@ -48,4 +53,6 @@ class ActionButton extends React.Component<ActionButtonProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export default ActionButton;
|
||||
export default withI18n({ id: 'actionButton', fallback: en, translations: { en } })(
|
||||
ActionButton,
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { RootState } from '../reducers';
|
||||
import { BLEConnectionState } from '../reducers/ble';
|
||||
import { BootloaderConnectionState } from '../reducers/bootloader';
|
||||
import ActionButton, { ActionButtonProps } from './ActionButton';
|
||||
import { TooltipId } from './button';
|
||||
import btConnectedIcon from './images/bt-connected.svg';
|
||||
import btDisconnectedIcon from './images/bt-disconnected.svg';
|
||||
|
||||
@@ -20,13 +21,13 @@ const mapStateToProps = (state: RootState): StateProps => {
|
||||
state.bootloader.connection === BootloaderConnectionState.Disconnected
|
||||
) {
|
||||
return {
|
||||
tooltip: 'Connect using Bluetooth',
|
||||
tooltip: TooltipId.BluetoothConnect,
|
||||
icon: btDisconnectedIcon,
|
||||
enabled: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
tooltip: 'Disconnect Bluetooth',
|
||||
tooltip: TooltipId.BluetoothDisconnect,
|
||||
icon: btConnectedIcon,
|
||||
enabled: state.ble.connection === BLEConnectionState.Connected,
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as notification from '../actions/notification';
|
||||
import { RootState } from '../reducers';
|
||||
import { BootloaderConnectionState } from '../reducers/bootloader';
|
||||
import OpenFileButton, { OpenFileButtonProps } from './OpenFileButton';
|
||||
import { TooltipId } from './button';
|
||||
import firmwareIcon from './images/firmware.svg';
|
||||
|
||||
type StateProps = Pick<OpenFileButtonProps, 'enabled'>;
|
||||
@@ -38,7 +39,7 @@ const mergeProps = (
|
||||
ownProps: OwnProps,
|
||||
): OpenFileButtonProps => ({
|
||||
fileExtension: '.zip',
|
||||
tooltip: 'Flash hub firmware',
|
||||
tooltip: TooltipId.Flash,
|
||||
icon: firmwareIcon,
|
||||
...ownProps,
|
||||
...stateProps,
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as editor from '../actions/editor';
|
||||
import * as notification from '../actions/notification';
|
||||
import { RootState } from '../reducers';
|
||||
import OpenFileButton, { OpenFileButtonProps } from './OpenFileButton';
|
||||
import { TooltipId } from './button';
|
||||
import openIcon from './images/open.svg';
|
||||
|
||||
type StateProps = Pick<OpenFileButtonProps, 'enabled'>;
|
||||
@@ -34,7 +35,7 @@ const mergeProps = (
|
||||
ownProps: OwnProps,
|
||||
): OpenFileButtonProps => ({
|
||||
fileExtension: '.py',
|
||||
tooltip: 'Open file',
|
||||
tooltip: TooltipId.Open,
|
||||
icon: openIcon,
|
||||
...ownProps,
|
||||
...stateProps,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { WithI18nProps, withI18n } from '@shopify/react-i18n';
|
||||
import React from 'react';
|
||||
import Button from 'react-bootstrap/Button';
|
||||
import Image from 'react-bootstrap/Image';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/Tooltip';
|
||||
import Dropzone, { FileRejection } from 'react-dropzone';
|
||||
import { TooltipId } from './button';
|
||||
import en from './button.en.json';
|
||||
|
||||
export interface OpenFileButtonProps {
|
||||
/** A unique id for each instance. */
|
||||
@@ -14,7 +17,7 @@ export interface OpenFileButtonProps {
|
||||
/** The accepted file extension */
|
||||
readonly fileExtension: string;
|
||||
/** Tooltip text that appears when hovering over the button. */
|
||||
readonly tooltip: string;
|
||||
readonly tooltip: TooltipId;
|
||||
/** Icon shown on the button. */
|
||||
readonly icon: string;
|
||||
/** When true or undefined, the button is enabled. */
|
||||
@@ -27,11 +30,13 @@ export interface OpenFileButtonProps {
|
||||
readonly onClick?: () => void;
|
||||
}
|
||||
|
||||
type Props = OpenFileButtonProps & WithI18nProps;
|
||||
|
||||
/**
|
||||
* Button that opens a file chooser dialog or accepts files dropped on it.
|
||||
*/
|
||||
class OpenFileButton extends React.Component<OpenFileButtonProps> {
|
||||
constructor(props: OpenFileButtonProps) {
|
||||
class OpenFileButton extends React.Component<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.onDropAccepted = this.onDropAccepted.bind(this);
|
||||
this.onDropRejected = this.onDropRejected.bind(this);
|
||||
@@ -80,7 +85,7 @@ class OpenFileButton extends React.Component<OpenFileButtonProps> {
|
||||
placement="bottom"
|
||||
overlay={
|
||||
<Tooltip id={`${this.props.id}-tooltip`}>
|
||||
{this.props.tooltip}.
|
||||
{this.props.i18n.translate(this.props.tooltip)}.
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
@@ -110,4 +115,6 @@ class OpenFileButton extends React.Component<OpenFileButtonProps> {
|
||||
}
|
||||
}
|
||||
|
||||
export default OpenFileButton;
|
||||
export default withI18n({ id: 'openFileButton', fallback: en, translations: { en } })(
|
||||
OpenFileButton,
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { repl } from '../actions/hub';
|
||||
import { RootState } from '../reducers';
|
||||
import { HubRuntimeState } from '../reducers/hub';
|
||||
import ActionButton, { ActionButtonProps } from './ActionButton';
|
||||
import { TooltipId } from './button';
|
||||
import replIcon from './images/repl.svg';
|
||||
|
||||
type StateProps = Pick<ActionButtonProps, 'enabled'>;
|
||||
@@ -28,7 +29,7 @@ const mergeProps = (
|
||||
dispatchProps: DispatchProps,
|
||||
ownProps: OwnProps,
|
||||
): ActionButtonProps => ({
|
||||
tooltip: 'Start REPL in terminal',
|
||||
tooltip: TooltipId.Repl,
|
||||
icon: replIcon,
|
||||
...ownProps,
|
||||
...stateProps,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { downloadAndRun } from '../actions/hub';
|
||||
import { RootState } from '../reducers';
|
||||
import { HubRuntimeState } from '../reducers/hub';
|
||||
import ActionButton, { ActionButtonProps } from './ActionButton';
|
||||
import { TooltipId } from './button';
|
||||
import runIcon from './images/run.svg';
|
||||
|
||||
type StateProps = Pick<ActionButtonProps, 'enabled'>;
|
||||
@@ -27,6 +28,7 @@ const mergeProps = (
|
||||
dispatchProps: DispatchProps,
|
||||
ownProps: OwnProps,
|
||||
): ActionButtonProps => ({
|
||||
tooltip: TooltipId.Run,
|
||||
icon: runIcon,
|
||||
...ownProps,
|
||||
...stateProps,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Dispatch } from '../actions';
|
||||
import * as editor from '../actions/editor';
|
||||
import { RootState } from '../reducers';
|
||||
import ActionButton, { ActionButtonProps } from './ActionButton';
|
||||
import { TooltipId } from './button';
|
||||
import downloadIcon from './images/download.svg';
|
||||
|
||||
type StateProps = Pick<ActionButtonProps, 'enabled'>;
|
||||
@@ -27,7 +28,7 @@ const mergeProps = (
|
||||
dispatchProps: DispatchProps,
|
||||
ownProps: OwnProps,
|
||||
): ActionButtonProps => ({
|
||||
tooltip: 'Download file',
|
||||
tooltip: TooltipId.SaveAs,
|
||||
icon: downloadIcon,
|
||||
...ownProps,
|
||||
...stateProps,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { stop } from '../actions/hub';
|
||||
import { RootState } from '../reducers';
|
||||
import { HubRuntimeState } from '../reducers/hub';
|
||||
import ActionButton, { ActionButtonProps } from './ActionButton';
|
||||
import { TooltipId } from './button';
|
||||
import stopIcon from './images/stop.svg';
|
||||
|
||||
type StateProps = Pick<ActionButtonProps, 'enabled'>;
|
||||
@@ -26,7 +27,7 @@ const mergeProps = (
|
||||
dispatchProps: DispatchProps,
|
||||
ownProps: OwnProps,
|
||||
): ActionButtonProps => ({
|
||||
tooltip: 'Stop everything',
|
||||
tooltip: TooltipId.Stop,
|
||||
icon: stopIcon,
|
||||
...ownProps,
|
||||
...stateProps,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"open": { "tooltip": "Open file" },
|
||||
"saveAs": { "tooltip": "Download file" },
|
||||
"stop": { "tooltip": "Stop everything" },
|
||||
"run": { "tooltip": "Download and run this program" },
|
||||
"repl": { "tooltip": "Start REPL in terminal" },
|
||||
"bluetooth": {
|
||||
"connect": { "tooltip": "Connect using Bluetooth" },
|
||||
"disconnect": { "tooltip": "Disconnect Bluetooth" }
|
||||
},
|
||||
"flash": { "tooltip": "Flash hub firmware" }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
import { TooltipId } from './button';
|
||||
import en from './button.en.json';
|
||||
|
||||
function lookup(obj: object, id: string): string | undefined {
|
||||
const value = id
|
||||
.split('.')
|
||||
.reduce((pv, cv) => pv && (pv as Record<string, object>)[cv], obj);
|
||||
if (typeof value === 'string') {
|
||||
return value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
describe('Ensure .json file has matches for TooltipIds', () => {
|
||||
test.each(Object.values(TooltipId))('%s', (id) => {
|
||||
expect(lookup(en, id)).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2020 The Pybricks Authors
|
||||
|
||||
export enum TooltipId {
|
||||
Open = 'open.tooltip',
|
||||
SaveAs = 'saveAs.tooltip',
|
||||
Run = 'run.tooltip',
|
||||
Stop = 'stop.tooltip',
|
||||
Repl = 'repl.tooltip',
|
||||
Flash = 'flash.tooltip',
|
||||
BluetoothConnect = 'bluetooth.connect.tooltip',
|
||||
BluetoothDisconnect = 'bluetooth.disconnect.tooltip',
|
||||
}
|
||||
Reference in New Issue
Block a user