From 41a1cee0923d7dfa12fb27870043f3ba778ea0e4 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 11 Jan 2021 20:14:35 -0600 Subject: [PATCH] change support button to settings button and add new empty settings drawer --- src/actions/app.ts | 28 ++++- src/components/App.tsx | 2 + src/components/SettingsButton.tsx | 30 ++++++ src/components/SettingsDrawer.tsx | 55 ++++++++++ src/components/SupportButton.tsx | 24 ----- src/components/Toolbar.tsx | 4 +- src/components/button-i18n.en.json | 2 +- src/components/button-i18n.ts | 2 +- src/components/images/settings.svg | 131 ++++++++++++++++++++++++ src/components/settings-i18n.en.json | 5 + src/components/settings-i18n.en.test.ts | 12 +++ src/components/settings-i18n.ts | 8 ++ src/reducers/app.ts | 16 ++- src/reducers/index.ts | 3 + src/reducers/settings.ts | 10 ++ src/sagas/app.test.ts | 4 +- 16 files changed, 302 insertions(+), 34 deletions(-) create mode 100644 src/components/SettingsButton.tsx create mode 100644 src/components/SettingsDrawer.tsx delete mode 100644 src/components/SupportButton.tsx create mode 100644 src/components/images/settings.svg create mode 100644 src/components/settings-i18n.en.json create mode 100644 src/components/settings-i18n.en.test.ts create mode 100644 src/components/settings-i18n.ts create mode 100644 src/reducers/settings.ts diff --git a/src/actions/app.ts b/src/actions/app.ts index 227b1c51..daaf204c 100644 --- a/src/actions/app.ts +++ b/src/actions/app.ts @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// Copyright (c) 2020 The Pybricks Authors +// Copyright (c) 2020-2021 The Pybricks Authors // File: actions/app.ts // Actions for the app in general. @@ -9,6 +9,10 @@ import { Action } from 'redux'; export enum AppActionType { /** The app has just ben started. */ Startup = 'app.action.startup', + /** Open settings dialog. */ + OpenSettings = 'app.action.openSettings', + /** Close settings dialog. */ + CloseSettings = 'app.action.closeSettings', /** Toggle documentation visibility. */ ToggleDocs = 'app.action.toggleDocs', } @@ -21,6 +25,22 @@ export function startup(): AppStartupAction { return { type: AppActionType.Startup }; } +/** Action to open the settings dialog. */ +export type AppOpenSettingsAction = Action; + +/** Creates an action to open the settings dialog. */ +export function openSettings(): AppOpenSettingsAction { + return { type: AppActionType.OpenSettings }; +} + +/** Action to close the settings dialog. */ +export type AppCloseSettingsAction = Action; + +/** Creates an action to close the settings dialog. */ +export function closeSettings(): AppCloseSettingsAction { + return { type: AppActionType.CloseSettings }; +} + /** Action to toggle documentation visibility. */ export type AppToggleDocsAction = Action; @@ -30,4 +50,8 @@ export function toggleDocs(): AppToggleDocsAction { } /** common type for all app actions. */ -export type AppAction = AppStartupAction | AppToggleDocsAction; +export type AppAction = + | AppStartupAction + | AppOpenSettingsAction + | AppCloseSettingsAction + | AppToggleDocsAction; diff --git a/src/components/App.tsx b/src/components/App.tsx index 3c0c90bc..f4490d82 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -6,6 +6,7 @@ import { useSelector } from 'react-redux'; import SplitterLayout from 'react-splitter-layout'; import { RootState } from '../reducers'; import Editor from './Editor'; +import SettingsDrawer from './SettingsDrawer'; import StatusBar from './StatusBar'; import Terminal from './Terminal'; import Toolbar from './Toolbar'; @@ -61,6 +62,7 @@ function App(): JSX.Element { )} + ); } diff --git a/src/components/SettingsButton.tsx b/src/components/SettingsButton.tsx new file mode 100644 index 00000000..7d4eafb4 --- /dev/null +++ b/src/components/SettingsButton.tsx @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { connect } from 'react-redux'; +import { Action, Dispatch } from '../actions'; +import { openSettings as openSettings } from '../actions/app'; +import ActionButton, { ActionButtonProps } from './ActionButton'; +import { TooltipId } from './button-i18n'; +import settingsIcon from './images/settings.svg'; + +type StateProps = undefined; +type DispatchProps = Pick; +type OwnProps = Pick; + +const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ + onAction: (): Action => dispatch(openSettings()), +}); + +const mergeProps = ( + _stateProps: StateProps, + dispatchProps: DispatchProps, + ownProps: OwnProps, +): ActionButtonProps => ({ + tooltip: TooltipId.Settings, + icon: settingsIcon, + ...dispatchProps, + ...ownProps, +}); + +export default connect(undefined, mapDispatchToProps, mergeProps)(ActionButton); diff --git a/src/components/SettingsDrawer.tsx b/src/components/SettingsDrawer.tsx new file mode 100644 index 00000000..70ac15ab --- /dev/null +++ b/src/components/SettingsDrawer.tsx @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { Drawer } from '@blueprintjs/core'; +import { WithI18nProps, withI18n } from '@shopify/react-i18n'; +import React from 'react'; +import { connect } from 'react-redux'; +import { Action, Dispatch } from '../actions'; +import { closeSettings } from '../actions/app'; +import { RootState } from '../reducers'; +import { SettingsStringId } from './settings-i18n'; +import en from './settings-i18n.en.json'; + +type StateProps = { + open: boolean; +}; + +type DispatchProps = { + onClose: () => void; +}; + +type SettingsProps = StateProps & DispatchProps & WithI18nProps; + +class SettingsDrawer extends React.PureComponent { + render(): JSX.Element { + const { i18n, open, onClose } = this.props; + return ( + onClose()} + > + ); + } +} + +const mapStateToProps = (state: RootState): StateProps => ({ + open: state.app.showSettings, +}); + +const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ + onClose: (): Action => dispatch(closeSettings()), +}); + +export default connect( + mapStateToProps, + mapDispatchToProps, +)( + withI18n({ + id: 'settings', + fallback: en, + translations: { en }, + })(SettingsDrawer), +); diff --git a/src/components/SupportButton.tsx b/src/components/SupportButton.tsx deleted file mode 100644 index 1886d89a..00000000 --- a/src/components/SupportButton.tsx +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// Copyright (c) 2020 The Pybricks Authors - -import { connect } from 'react-redux'; -import LinkButton, { LinkButtonProps } from './LinkButton'; -import { TooltipId } from './button-i18n'; -import supportIcon from './images/support.svg'; - -type StateProps = undefined; -type DispatchProps = undefined; -type OwnProps = Pick; - -const mergeProps = ( - _stateProps: StateProps, - _dispatchProps: DispatchProps, - ownProps: OwnProps, -): LinkButtonProps => ({ - url: 'https://github.com/pybricks/support/issues', - tooltip: TooltipId.Support, - icon: supportIcon, - ...ownProps, -}); - -export default connect(undefined, undefined, mergeProps)(LinkButton); diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx index db49051c..d4bfb6e9 100644 --- a/src/components/Toolbar.tsx +++ b/src/components/Toolbar.tsx @@ -10,8 +10,8 @@ import OpenButton from './OpenButton'; import ReplButton from './ReplButton'; import RunButton from './RunButton'; import SaveAsButton from './SaveAsButton'; +import SettingsButton from './SettingsButton'; import StopButton from './StopButton'; -import SupportButton from './SupportButton'; class Toolbar extends React.Component { render(): JSX.Element { @@ -39,7 +39,7 @@ class Toolbar extends React.Component { - + diff --git a/src/components/button-i18n.en.json b/src/components/button-i18n.en.json index 077e559f..78d2547b 100644 --- a/src/components/button-i18n.en.json +++ b/src/components/button-i18n.en.json @@ -10,5 +10,5 @@ }, "flash": { "tooltip": "Flash hub firmware" }, "docs": { "tooltip": "Show/hide documentation" }, - "support": { "tooltip": "Open Pybricks Support web site" } + "settings": { "tooltip": "Open setting" } } diff --git a/src/components/button-i18n.ts b/src/components/button-i18n.ts index 831c6b61..fbad5705 100644 --- a/src/components/button-i18n.ts +++ b/src/components/button-i18n.ts @@ -13,5 +13,5 @@ export enum TooltipId { BluetoothConnect = 'bluetooth.connect.tooltip', BluetoothDisconnect = 'bluetooth.disconnect.tooltip', Docs = 'docs.tooltip', - Support = 'support.tooltip', + Settings = 'settings.tooltip', } diff --git a/src/components/images/settings.svg b/src/components/images/settings.svg new file mode 100644 index 00000000..559cd5f1 --- /dev/null +++ b/src/components/images/settings.svg @@ -0,0 +1,131 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/settings-i18n.en.json b/src/components/settings-i18n.en.json new file mode 100644 index 00000000..903e298a --- /dev/null +++ b/src/components/settings-i18n.en.json @@ -0,0 +1,5 @@ +{ + "settings": { + "title": "Settings" + } +} diff --git a/src/components/settings-i18n.en.test.ts b/src/components/settings-i18n.en.test.ts new file mode 100644 index 00000000..679896e4 --- /dev/null +++ b/src/components/settings-i18n.en.test.ts @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { lookup } from '../../test'; +import { SettingsStringId } from './settings-i18n'; +import en from './settings-i18n.en.json'; + +describe('Ensure .json file has matches for SettingsStringIds', () => { + test.each(Object.values(SettingsStringId))('%s', (id) => { + expect(lookup(en, id)).toBeDefined(); + }); +}); diff --git a/src/components/settings-i18n.ts b/src/components/settings-i18n.ts new file mode 100644 index 00000000..dfc23355 --- /dev/null +++ b/src/components/settings-i18n.ts @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors +// File: components/settings-i18n.ts +// Settings translation keys. + +export enum SettingsStringId { + Title = 'settings.title', +} diff --git a/src/reducers/app.ts b/src/reducers/app.ts index 6b70b2b2..2b2171ee 100644 --- a/src/reducers/app.ts +++ b/src/reducers/app.ts @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -// Copyright (c) 2020 The Pybricks Authors +// Copyright (c) 2020-2021 The Pybricks Authors // File: reducers/app.ts // Manages state the app in general. @@ -7,6 +7,17 @@ import { Reducer, combineReducers } from 'redux'; import { Action } from '../actions'; import { AppActionType } from '../actions/app'; +const showSettings: Reducer = (state = false, action) => { + switch (action.type) { + case AppActionType.OpenSettings: + return true; + case AppActionType.CloseSettings: + return false; + default: + return state; + } +}; + const showDocs: Reducer = (state = false, action) => { switch (action.type) { case AppActionType.ToggleDocs: @@ -17,7 +28,8 @@ const showDocs: Reducer = (state = false, action) => { }; export interface AppState { + readonly showSettings: boolean; readonly showDocs: boolean; } -export default combineReducers({ showDocs }); +export default combineReducers({ showSettings, showDocs }); diff --git a/src/reducers/index.ts b/src/reducers/index.ts index 832c8a9a..a6ccb937 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -8,6 +8,7 @@ import bootloader, { BootloaderState } from './bootloader'; import editor, { EditorState } from './editor'; import hub, { HubState } from './hub'; import notification, { NotificationState } from './notification'; +import settings, { SettingsState } from './settings'; import status, { StatusState } from './status'; import terminal, { TerminalState } from './terminal'; @@ -21,6 +22,7 @@ export interface RootState { readonly editor: EditorState; readonly hub: HubState; readonly notification: NotificationState; + readonly settings: SettingsState; readonly status: StatusState; readonly terminal: TerminalState; } @@ -32,6 +34,7 @@ export default combineReducers({ editor, hub, notification, + settings, status, terminal, }); diff --git a/src/reducers/settings.ts b/src/reducers/settings.ts new file mode 100644 index 00000000..fe0dba5b --- /dev/null +++ b/src/reducers/settings.ts @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2021 The Pybricks Authors + +import { combineReducers } from 'redux'; + +export interface SettingsState { + readonly visible: boolean; +} + +export default combineReducers({}); diff --git a/src/sagas/app.test.ts b/src/sagas/app.test.ts index 0030133c..77ef1431 100644 --- a/src/sagas/app.test.ts +++ b/src/sagas/app.test.ts @@ -88,7 +88,7 @@ describe('storeDocsState', () => { const mockSetItem = jest .spyOn(Object.getPrototypeOf(window.localStorage), 'setItem') .mockImplementation((_key, value) => expect(value).toBe('true')); - saga.setState({ app: { showDocs: true } }); + saga.setState({ app: { showSettings: false, showDocs: true } }); saga.put(toggleDocs()); expect(mockSetItem).toHaveBeenCalled(); @@ -101,7 +101,7 @@ describe('storeDocsState', () => { const mockSetItem = jest .spyOn(Object.getPrototypeOf(window.localStorage), 'setItem') .mockImplementation((_key, value) => expect(value).toBe('false')); - saga.setState({ app: { showDocs: false } }); + saga.setState({ app: { showSettings: false, showDocs: false } }); saga.put(toggleDocs()); expect(mockSetItem).toHaveBeenCalled();