- Lorem ipsum dolor sit amet
+const SampleContent = () => (
+ <>
+ Lorem ipsum dolor sit amet
{[...Array(10)].map((_, i) => (
At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis
@@ -26,5 +26,32 @@ export const WithContent = () => (
consequatur aut perferendis doloribus asperiores repellat.
))}
+ >
+);
+
+export const Default = () => ;
+
+export const WithContent = () => (
+
+
);
+
+export const Collapsed = (ctx: StoryContext) => {
+ useEffect(() => {
+ // grab the store from the Storybook parameter defined in preview.tsx
+ const store = ctx.parameters.store as Store;
+ store.sidebarCollapsed = true;
+
+ // change back to expanded when the component is unmounted
+ return () => {
+ store.sidebarCollapsed = false;
+ };
+ }, []);
+
+ return (
+
+
+
+ );
+};
diff --git a/app/src/action/app.ts b/app/src/action/app.ts
new file mode 100644
index 00000000..28026b96
--- /dev/null
+++ b/app/src/action/app.ts
@@ -0,0 +1,25 @@
+import { action, toJS } from 'mobx';
+import { log } from 'util/log';
+import { Store } from 'store';
+
+/**
+ * Action used to update app level state
+ */
+class AppAction {
+ private _store: Store;
+
+ constructor(store: Store) {
+ this._store = store;
+ }
+
+ /**
+ * toggle the sidebar to be collapsed or expanded
+ */
+ @action.bound toggleSidebar() {
+ log.info('toggling sidebar');
+ this._store.sidebarCollapsed = !this._store.sidebarCollapsed;
+ log.info('updated store.sidebarCollapsed', toJS(this._store.sidebarCollapsed));
+ }
+}
+
+export default AppAction;
diff --git a/app/src/action/index.ts b/app/src/action/index.ts
index c09abe66..eb0b930d 100644
--- a/app/src/action/index.ts
+++ b/app/src/action/index.ts
@@ -2,11 +2,13 @@ import GrpcClient from 'api/grpc';
import LndApi from 'api/lnd';
import LoopApi from 'api/loop';
import { Store } from 'store';
+import AppAction from './app';
import ChannelAction from './channel';
import NodeAction from './node';
import SwapAction from './swap';
export interface StoreActions {
+ app: AppAction;
node: NodeAction;
channel: ChannelAction;
swap: SwapAction;
@@ -15,19 +17,22 @@ export interface StoreActions {
/**
* Creates actions that modify the state of the given mobx store
* @param store the Store instance that the actions will modify
+ * @param grpcClient optionally provide an alternate grpc client if necessary
*/
-export const createActions = (store: Store): StoreActions => {
+export const createActions = (store: Store, grpcClient?: GrpcClient): StoreActions => {
// low level dependencies
- const grpc = new GrpcClient();
+ const grpc = grpcClient || new GrpcClient();
const lndApi = new LndApi(grpc);
const loopApi = new LoopApi(grpc);
// actions exposed to UI components
+ const app = new AppAction(store);
const node = new NodeAction(store, lndApi);
const channel = new ChannelAction(store, lndApi);
const swap = new SwapAction(store, loopApi);
return {
+ app,
node,
channel,
swap,
diff --git a/app/src/components/layout/Layout.tsx b/app/src/components/layout/Layout.tsx
index 75db0454..8e166b64 100644
--- a/app/src/components/layout/Layout.tsx
+++ b/app/src/components/layout/Layout.tsx
@@ -1,9 +1,15 @@
import React from 'react';
+import { observer } from 'mobx-react-lite';
+import { useActions, useStore } from 'store';
import { Background } from 'components/common/base';
import { Menu } from 'components/common/icons';
import { styled } from 'components/theme';
import Sidebar from './Sidebar';
+interface CollapsedProps {
+ collapsed: boolean;
+}
+
const Styled = {
Container: styled.div`
position: relative;
@@ -18,30 +24,43 @@ const Styled = {
z-index: 1;
cursor: pointer;
`,
- Aside: styled.aside`
+ Aside: styled.aside`
position: fixed;
top: 0;
height: 100vh;
background-color: ${props => props.theme.colors.darkBlue};
- width: 285px;
- padding: 15px;
+ overflow: hidden;
+
+ /* change sidebar dimensions based on collapsed toggle */
+ width: ${props => (props.collapsed ? '0' : '285px')};
+ padding: ${props => (props.collapsed ? '0' : '15px')};
+ transition: all 0.2s;
+
+ /* set a width on the child to improve the collapse animation */
+ & > div {
+ width: 285px;
+ }
`,
- Content: styled.div`
- margin-left: 285px;
+ Content: styled.div`
+ margin-left: ${props => (props.collapsed ? '0' : '285px')};
padding: 15px;
+ transition: all 0.2s;
`,
};
const Layout: React.FC = ({ children }) => {
+ const { sidebarCollapsed } = useStore();
+ const { app } = useActions();
+
const { Container, MenuIcon, Aside, Content } = Styled;
return (
-
-
@@ -49,4 +68,4 @@ const Layout: React.FC = ({ children }) => {
);
};
-export default Layout;
+export default observer(Layout);
diff --git a/app/src/store/index.ts b/app/src/store/index.ts
index cc2639e6..b880e431 100644
--- a/app/src/store/index.ts
+++ b/app/src/store/index.ts
@@ -5,6 +5,9 @@ import { Channel, NodeBalances, NodeInfo, Swap } from 'types/state';
* The store used to manage global app state
*/
export class Store {
+ // App state
+ @observable sidebarCollapsed = false;
+ // API data
@observable info?: NodeInfo = undefined;
@observable balances?: NodeBalances = undefined;
@observable channels: Channel[] = [];