mirror of
https://github.com/stake-house/wagyu-installer.git
synced 2022-05-06 20:09:49 +03:00
[WAGYU-56-59] Update directory structure, housekeeping, storybook, and more (#65)
* run formatter * remove vscode settings from gitignore * add storybook * add polished * directly import colors * add common Header typography component * fix footer export * fix App import / export * use new Header * format main.ts
This commit is contained in:
committed by
GitHub
parent
e221ab7432
commit
f236bca658
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,3 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.idea
|
||||
|
||||
# Ignore Visual Studio Code IDE workspace settings
|
||||
.vscode/
|
||||
14
.storybook/decorators/withGlobalStyles.tsx
Normal file
14
.storybook/decorators/withGlobalStyles.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Container = styled.main`
|
||||
font-family: 'PT Mono', monospace;
|
||||
`;
|
||||
|
||||
export const withGlobalStyles = (storyFn: any) => {
|
||||
return (
|
||||
<>
|
||||
<Container>{storyFn()}</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
4
.storybook/main.ts
Normal file
4
.storybook/main.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
addons: ['@storybook/addon-links', '@storybook/addon-essentials'],
|
||||
};
|
||||
14
.storybook/preview.ts
Normal file
14
.storybook/preview.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { addDecorator } from '@storybook/react';
|
||||
import { withGlobalStyles } from './decorators/withGlobalStyles';
|
||||
|
||||
addDecorator(withGlobalStyles);
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
};
|
||||
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"workbench.tree.renderIndentGuides": "always",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"eslint.options": {
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"prefer-const": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
package.json
12
package.json
@@ -6,11 +6,17 @@
|
||||
"author": "Colfax Selby <colfax.selby@gmail.com>",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
"@storybook/addon-actions": "^6.3.7",
|
||||
"@storybook/addon-essentials": "^6.3.7",
|
||||
"@storybook/addon-links": "^6.3.7",
|
||||
"@storybook/react": "^6.3.7",
|
||||
"@types/js-yaml": "^4.0.0",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.1",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"@types/styled-components": "^5.1.7",
|
||||
"babel-loader": "^8.2.2",
|
||||
"electron": "^12.0.0",
|
||||
"ts-loader": "^8.0.17",
|
||||
"typescript": "^4.2.2",
|
||||
@@ -23,15 +29,19 @@
|
||||
"build:watch": "yarn build -- --watch",
|
||||
"start": "electron ./dist/electron/index.js",
|
||||
"dev:electron": "NODE_ENV=development webpack --config webpack.electron.config.js --mode development && electron .",
|
||||
"dev:react": "NODE_ENV=development webpack-serve --config webpack.react.config.js --mode development"
|
||||
"dev:react": "NODE_ENV=development webpack-serve --config webpack.react.config.js --mode development",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rauschma/stringio": "^1.4.0",
|
||||
"html-webpack-plugin": "^5.2.0",
|
||||
"js-yaml": "^4.0.0",
|
||||
"polished": "^4.1.3",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"sb": "^6.3.7",
|
||||
"shebang-loader": "^0.0.1",
|
||||
"styled-components": "^5.2.1"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BrowserWindow, app, globalShortcut } from "electron";
|
||||
import { BrowserWindow, app, globalShortcut } from 'electron';
|
||||
|
||||
app.on("ready", () => {
|
||||
app.on('ready', () => {
|
||||
// once electron has started up, create a window.
|
||||
const window = new BrowserWindow({
|
||||
width: 900,
|
||||
@@ -12,18 +12,17 @@ app.on("ready", () => {
|
||||
|
||||
// TODO: is it a problem to disable this?
|
||||
// https://www.electronjs.org/docs/tutorial/context-isolation#security-considerations
|
||||
contextIsolation: false
|
||||
}
|
||||
contextIsolation: false,
|
||||
},
|
||||
});
|
||||
|
||||
// hide the default menu bar that comes with the browser window
|
||||
window.setMenuBarVisibility(false);
|
||||
|
||||
|
||||
globalShortcut.register('CommandOrControl+R', function() {
|
||||
console.log('CommandOrControl+R is pressed')
|
||||
window.reload()
|
||||
})
|
||||
globalShortcut.register('CommandOrControl+R', function () {
|
||||
console.log('CommandOrControl+R is pressed');
|
||||
window.reload();
|
||||
});
|
||||
|
||||
// load a website to display
|
||||
window.loadURL(`file://${__dirname}/../react/index.html`);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { HashRouter, Route, Switch } from 'react-router-dom';
|
||||
import { Background } from './colors';
|
||||
import { Slate1 } from './colors';
|
||||
import { Deposit } from './components/Deposit';
|
||||
import { Home } from './components/Home';
|
||||
import { InstallFailed } from './components/InstallFailed';
|
||||
import Installing from './components/Installing';
|
||||
import Status from './components/Status';
|
||||
import { Installing } from './components/Installing';
|
||||
import { StatusPage as Status } from './components/Status';
|
||||
import { SystemCheck } from './components/SystemCheck';
|
||||
|
||||
const Container = styled.main`
|
||||
@@ -14,10 +14,10 @@ const Container = styled.main`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background-color: ${Background};
|
||||
background-color: ${Slate1};
|
||||
`;
|
||||
|
||||
const App = () => {
|
||||
export const App = () => {
|
||||
return (
|
||||
<HashRouter>
|
||||
<Container>
|
||||
@@ -33,5 +33,3 @@ const App = () => {
|
||||
</HashRouter>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -1,30 +1,21 @@
|
||||
export const Gray1 = "#F1F6FE";
|
||||
export const Gray2 = "#E3EBF7";
|
||||
export const Gray3 = "#d0dae8";
|
||||
export const Gray4 = "#ACBED5";
|
||||
export const DarkGray = "#A9A9A9";
|
||||
export const White = '#FFFFFF';
|
||||
export const Gray1 = '#F1F6FE';
|
||||
export const Gray2 = '#E3EBF7';
|
||||
export const Gray3 = '#d0dae8';
|
||||
export const Gray4 = '#ACBED5';
|
||||
export const DarkGray = '#A9A9A9';
|
||||
export const Black = '#000000';
|
||||
|
||||
export const TextDark = "#0B1E58";
|
||||
|
||||
export const PrimaryBlue = "#3366FF";
|
||||
export const PrimaryBlueDark = "#174BE6";
|
||||
export const TextDark = '#0B1E58';
|
||||
export const PrimaryBlue = '#3366FF';
|
||||
export const PrimaryBlueDark = '#174BE6';
|
||||
|
||||
// https://colorhunt.co/palette/167893
|
||||
export const DarkBlue = "#0f4c75";
|
||||
export const MediumBlue = "#3282b8";
|
||||
export const LightBlue = "#bbe1fa";
|
||||
export const White = "#FFFFFF";
|
||||
export const Red = "#fa1e0e";
|
||||
|
||||
export const LightGreen = "#52b788";
|
||||
|
||||
export const Background = "#1b262c";
|
||||
export const Button = LightBlue;
|
||||
export const ButtonHover = LightGreen;
|
||||
export const Heading = MediumBlue;
|
||||
export const MainContent = Gray3;
|
||||
export const MainContentAlert = Red;
|
||||
export const Black = "#000000"
|
||||
export const DisabledButton = DarkGray;
|
||||
export const DarkBlue = '#0F4C75';
|
||||
export const MediumBlue = '#3282B8';
|
||||
export const LightBlue = '#BBE1FA';
|
||||
export const Red = '#FA1E0E';
|
||||
|
||||
export const LightGreen = '#52B788';
|
||||
|
||||
export const Slate1 = '#1B262C';
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
import { executeCommandSync, executeCommandSyncReturnStdout } from "./ExecuteCommand";
|
||||
import {
|
||||
executeCommandSync,
|
||||
executeCommandSyncReturnStdout,
|
||||
} from './ExecuteCommand';
|
||||
|
||||
const doesFileExist = (filename: string): boolean => {
|
||||
const cmd = "test -f " + filename;
|
||||
export const doesFileExist = (filename: string): boolean => {
|
||||
const cmd = 'test -f ' + filename;
|
||||
const result = executeCommandSync(cmd);
|
||||
return result == 0;
|
||||
};
|
||||
|
||||
//TODO: add error handling
|
||||
const readlink = (file: string): string => {
|
||||
return executeCommandSyncReturnStdout("readlink -f " + file).trim();
|
||||
}
|
||||
// TODO: add error handling
|
||||
export const readlink = (file: string): string => {
|
||||
return executeCommandSyncReturnStdout('readlink -f ' + file).trim();
|
||||
};
|
||||
|
||||
const which = (tool: string): boolean => {
|
||||
const cmd = "which " + tool;
|
||||
export const which = (tool: string): boolean => {
|
||||
const cmd = 'which ' + tool;
|
||||
const result = executeCommandSync(cmd);
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
export {
|
||||
doesFileExist,
|
||||
readlink,
|
||||
which
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { exec, execSync, spawn } from 'child_process';
|
||||
import { streamEnd, streamWrite } from '@rauschma/stringio';
|
||||
|
||||
import { Writable } from 'stream';
|
||||
|
||||
// TODO: better error handling and logging
|
||||
// TODO: remove console.log
|
||||
|
||||
// TODO: make this work for different operating systems
|
||||
const UBUNTU_TERMINAL_COMMAND = "/usr/bin/gnome-terminal";
|
||||
const UBUNTU_TERMINAL_COMMAND = '/usr/bin/gnome-terminal';
|
||||
|
||||
type StdoutCallback = (text: string) => void;
|
||||
|
||||
const executeCommandAsync = async (cmd: string): Promise<any> => {
|
||||
console.log("running command async with: " + cmd);
|
||||
console.log('running command async with: ' + cmd);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = exec(cmd);
|
||||
@@ -20,25 +19,31 @@ const executeCommandAsync = async (cmd: string): Promise<any> => {
|
||||
child.once('exit', function (code) {
|
||||
resolve(code);
|
||||
});
|
||||
|
||||
|
||||
child.on('error', function (err) {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const executeCommandInNewTerminal = (cmd: string, title: string): number => {
|
||||
return executeCommandSync(UBUNTU_TERMINAL_COMMAND + " --title=\"" + title + "\" -- bash -c '" + cmd + "'");
|
||||
}
|
||||
return executeCommandSync(
|
||||
UBUNTU_TERMINAL_COMMAND +
|
||||
' --title="' +
|
||||
title +
|
||||
'" -- bash -c \'' +
|
||||
cmd +
|
||||
"'",
|
||||
);
|
||||
};
|
||||
|
||||
const executeCommandSync = (cmd: string): number => {
|
||||
console.log("running command sync with: " + cmd);
|
||||
console.log('running command sync with: ' + cmd);
|
||||
|
||||
try {
|
||||
execSync(cmd, {stdio: 'inherit'});
|
||||
execSync(cmd, { stdio: 'inherit' });
|
||||
return 0;
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
// TODO: more robust error handling
|
||||
error.status;
|
||||
error.message;
|
||||
@@ -47,15 +52,14 @@ const executeCommandSync = (cmd: string): number => {
|
||||
console.log(error.message);
|
||||
return error.status;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const executeCommandSyncReturnStdout = (cmd: string): string => {
|
||||
console.log("running command sync stdout with: " + cmd);
|
||||
console.log('running command sync stdout with: ' + cmd);
|
||||
|
||||
try {
|
||||
return execSync(cmd).toString();
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
// TODO: more robust error handling
|
||||
error.status;
|
||||
error.message;
|
||||
@@ -64,13 +68,16 @@ const executeCommandSyncReturnStdout = (cmd: string): string => {
|
||||
console.log(error.message);
|
||||
return error.message;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const executeCommandStream = (cmd: string, stdoutCallback: StdoutCallback): Promise<any> => {
|
||||
console.log("running command stream with: " + cmd);
|
||||
const executeCommandStream = (
|
||||
cmd: string,
|
||||
stdoutCallback: StdoutCallback,
|
||||
): Promise<any> => {
|
||||
console.log('running command stream with: ' + cmd);
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(cmd, {
|
||||
shell: true
|
||||
shell: true,
|
||||
});
|
||||
|
||||
child.stdout.on('data', (data: Buffer) => {
|
||||
@@ -89,12 +96,20 @@ const executeCommandStream = (cmd: string, stdoutCallback: StdoutCallback): Prom
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// good resource for this: https://2ality.com/2018/05/child-process-streams.html
|
||||
const executeCommandWithPromptsAsync = (cmd: string, responses: string[], stdoutCallback: StdoutCallback): Promise<any> => {
|
||||
console.log("running command with prompts async with: " + cmd + " and responses " + responses.join());
|
||||
const executeCommandWithPromptsAsync = (
|
||||
cmd: string,
|
||||
responses: string[],
|
||||
stdoutCallback: StdoutCallback,
|
||||
): Promise<any> => {
|
||||
console.log(
|
||||
'running command with prompts async with: ' +
|
||||
cmd +
|
||||
' and responses ' +
|
||||
responses.join(),
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(cmd, {
|
||||
@@ -111,19 +126,19 @@ const executeCommandWithPromptsAsync = (cmd: string, responses: string[], stdout
|
||||
child.once('exit', function (code) {
|
||||
resolve(code);
|
||||
});
|
||||
|
||||
|
||||
child.on('error', function (err) {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: using this sync wait to get the prompt responses to work properly is
|
||||
// probably not the best - come up with alternative solution
|
||||
const syncWait = (ms: number) => {
|
||||
const end = Date.now() + ms
|
||||
while (Date.now() < end) continue
|
||||
}
|
||||
const end = Date.now() + ms;
|
||||
while (Date.now() < end) continue;
|
||||
};
|
||||
|
||||
async function writeToWritable(writable: Writable, responses: string[]) {
|
||||
syncWait(1000);
|
||||
@@ -143,4 +158,4 @@ export {
|
||||
executeCommandSync,
|
||||
executeCommandSyncReturnStdout,
|
||||
executeCommandWithPromptsAsync,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
import { doesFileExist, readlink } from "./BashUtils";
|
||||
import fs from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import { doesFileExist, readlink } from './BashUtils';
|
||||
import {
|
||||
executeCommandAsync,
|
||||
executeCommandInNewTerminal,
|
||||
executeCommandStream,
|
||||
executeCommandSyncReturnStdout,
|
||||
executeCommandWithPromptsAsync,
|
||||
} from './ExecuteCommand'
|
||||
} from './ExecuteCommand';
|
||||
import { Status } from '../types';
|
||||
|
||||
import fs from "fs";
|
||||
import yaml from "js-yaml";
|
||||
import { Status } from "../types";
|
||||
const ASKPASS_PATH = 'src/scripts/askpass.sh';
|
||||
const ROCKET_POOL_EXECUTABLE = '~/bin/rocketpool';
|
||||
const ROCKET_POOL_DIR = '~/.rocketpool';
|
||||
const ROCKET_POOL_INSTALL_COMMAND =
|
||||
'mkdir -p ~/bin && wget https://github.com/rocket-pool/smartnode-install/releases/download/1.0.0-beta.3/rocketpool-cli-linux-amd64 -O ' +
|
||||
ROCKET_POOL_EXECUTABLE +
|
||||
' && chmod +x ' +
|
||||
ROCKET_POOL_EXECUTABLE;
|
||||
|
||||
const ASKPASS_PATH = "src/scripts/askpass.sh";
|
||||
|
||||
const ROCKET_POOL_EXECUTABLE = "~/bin/rocketpool";
|
||||
const ROCKET_POOL_DIR = "~/.rocketpool"
|
||||
const ROCKET_POOL_INSTALL_COMMAND = "mkdir -p ~/bin && wget https://github.com/rocket-pool/smartnode-install/releases/download/1.0.0-beta.3/rocketpool-cli-linux-amd64 -O " + ROCKET_POOL_EXECUTABLE + " && chmod +x " + ROCKET_POOL_EXECUTABLE;
|
||||
|
||||
const GETH_SYNC_STATUS_DOCKER_CMD = "docker exec rocketpool_eth1 geth --exec 'eth.syncing' attach ipc:ethclient/geth/geth.ipc";
|
||||
const GETH_PEERS_DOCKER_CMD = "docker exec rocketpool_eth1 geth --exec 'admin.peers.length' attach ipc:ethclient/geth/geth.ipc";
|
||||
const GETH_SYNC_STATUS_DOCKER_CMD =
|
||||
"docker exec rocketpool_eth1 geth --exec 'eth.syncing' attach ipc:ethclient/geth/geth.ipc";
|
||||
const GETH_PEERS_DOCKER_CMD =
|
||||
"docker exec rocketpool_eth1 geth --exec 'admin.peers.length' attach ipc:ethclient/geth/geth.ipc";
|
||||
|
||||
// TODO: make an installer interface and implement it here, so we can easily extend
|
||||
// to utilize multiple different installers
|
||||
@@ -30,165 +34,216 @@ type NodeStatusCallback = (status: Status) => void;
|
||||
type StdoutCallback = (text: string[]) => void;
|
||||
|
||||
const wrapCommandInDockerGroup = (command: string) => {
|
||||
return "sg docker \"" + command + "\"";
|
||||
}
|
||||
return 'sg docker "' + command + '"';
|
||||
};
|
||||
|
||||
// TODO: make this better, it is pretty brittle and peeks into the RP settings implementation
|
||||
// this is required because we select the client at random, so we need to show the user what is running
|
||||
const getEth2ClientName = (): string => {
|
||||
try {
|
||||
const rpSettings: any = yaml.load(fs.readFileSync(readlink(ROCKET_POOL_DIR + '/settings.yml'), 'utf8'));
|
||||
const selectedClient = rpSettings["chains"]["eth2"]["client"]["selected"];
|
||||
const rpSettings: any = yaml.load(
|
||||
fs.readFileSync(readlink(ROCKET_POOL_DIR + '/settings.yml'), 'utf8'),
|
||||
);
|
||||
const selectedClient = rpSettings['chains']['eth2']['client']['selected'];
|
||||
|
||||
return selectedClient;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return "";
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const installAndStartRocketPool = async (callback: Callback, stdoutCallback: StdoutCallback) => {
|
||||
const installAndStartRocketPool = async (
|
||||
callback: Callback,
|
||||
stdoutCallback: StdoutCallback,
|
||||
) => {
|
||||
// Used for reporting back log messages to caller
|
||||
// TODO: there has to be a better way to do this...
|
||||
const consoleMessages: string[] = [];
|
||||
const internalStdoutCallback = (text: string) => {
|
||||
consoleMessages.push(text);
|
||||
stdoutCallback(consoleMessages);
|
||||
}
|
||||
};
|
||||
|
||||
// cache sudo credentials to be used for install later
|
||||
const passwordRc = await executeCommandStream("export SUDO_ASKPASS='" + ASKPASS_PATH + "' && sudo -A echo 'Authentication successful.'", internalStdoutCallback);
|
||||
const passwordRc = await executeCommandStream(
|
||||
"export SUDO_ASKPASS='" +
|
||||
ASKPASS_PATH +
|
||||
"' && sudo -A echo 'Authentication successful.'",
|
||||
internalStdoutCallback,
|
||||
);
|
||||
if (passwordRc != 0) {
|
||||
console.log("password failed");
|
||||
console.log('password failed');
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const cliRc = await executeCommandStream(ROCKET_POOL_INSTALL_COMMAND, internalStdoutCallback);
|
||||
const cliRc = await executeCommandStream(
|
||||
ROCKET_POOL_INSTALL_COMMAND,
|
||||
internalStdoutCallback,
|
||||
);
|
||||
if (cliRc != 0) {
|
||||
console.log("cli failed to install");
|
||||
console.log('cli failed to install');
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const serviceRc = await executeCommandStream(ROCKET_POOL_EXECUTABLE + " service install --yes --network pyrmont", internalStdoutCallback);
|
||||
const serviceRc = await executeCommandStream(
|
||||
ROCKET_POOL_EXECUTABLE + ' service install --yes --network pyrmont',
|
||||
internalStdoutCallback,
|
||||
);
|
||||
if (serviceRc != 0) {
|
||||
console.log("service install failed");
|
||||
console.log('service install failed');
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// For some reason executeCommandWithPromptsAsync needs the full path, so fetching it here
|
||||
const rocketPoolExecutableFullPath = readlink(ROCKET_POOL_EXECUTABLE);
|
||||
console.log("full path");
|
||||
console.log('full path');
|
||||
console.log(rocketPoolExecutableFullPath);
|
||||
|
||||
const promptResponses = [
|
||||
"1\n", // which eth1 client? 1 geth, 2 infura, 3 custom
|
||||
"\n", // ethstats label
|
||||
"\n", // ethstats login
|
||||
"\n", // Cache size
|
||||
"\n", // Max peers
|
||||
"\n", // P2P port
|
||||
"y\n", // random eth2 client? y/n
|
||||
"\n", // graffiti
|
||||
"\n", // Max peers
|
||||
"\n", // P2P port
|
||||
]
|
||||
'1\n', // which eth1 client? 1 geth, 2 infura, 3 custom
|
||||
'\n', // ethstats label
|
||||
'\n', // ethstats login
|
||||
'\n', // Cache size
|
||||
'\n', // Max peers
|
||||
'\n', // P2P port
|
||||
'y\n', // random eth2 client? y/n
|
||||
'\n', // graffiti
|
||||
'\n', // Max peers
|
||||
'\n', // P2P port
|
||||
];
|
||||
|
||||
const serviceConfigRc = await executeCommandWithPromptsAsync(rocketPoolExecutableFullPath + " service config", promptResponses, internalStdoutCallback);
|
||||
const serviceConfigRc = await executeCommandWithPromptsAsync(
|
||||
rocketPoolExecutableFullPath + ' service config',
|
||||
promptResponses,
|
||||
internalStdoutCallback,
|
||||
);
|
||||
if (serviceConfigRc != 0) {
|
||||
console.log("service config failed");
|
||||
console.log('service config failed');
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Just in case nodes were running - pick up new config (might happen anyway on start, not sure)
|
||||
const stopNodesRc = await executeCommandStream(wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + " service stop -y"), internalStdoutCallback);
|
||||
const stopNodesRc = await executeCommandStream(
|
||||
wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + ' service stop -y'),
|
||||
internalStdoutCallback,
|
||||
);
|
||||
if (stopNodesRc != 0) {
|
||||
console.log("stop nodes failed");
|
||||
console.log('stop nodes failed');
|
||||
callback(false);
|
||||
}
|
||||
|
||||
const startNodesRc = await executeCommandStream(wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + " service start"), internalStdoutCallback);
|
||||
const startNodesRc = await executeCommandStream(
|
||||
wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + ' service start'),
|
||||
internalStdoutCallback,
|
||||
);
|
||||
if (startNodesRc != 0) {
|
||||
console.log("start nodes failed");
|
||||
console.log('start nodes failed');
|
||||
callback(false);
|
||||
}
|
||||
|
||||
await executeCommandStream("echo 'Install complete - redirecting...'", internalStdoutCallback);
|
||||
await executeCommandStream(
|
||||
"echo 'Install complete - redirecting...'",
|
||||
internalStdoutCallback,
|
||||
);
|
||||
|
||||
callback(true);
|
||||
}
|
||||
};
|
||||
|
||||
const isRocketPoolInstalled = (): boolean => {
|
||||
return doesFileExist(ROCKET_POOL_EXECUTABLE)
|
||||
}
|
||||
return doesFileExist(ROCKET_POOL_EXECUTABLE);
|
||||
};
|
||||
|
||||
const openEth1Logs = () => {
|
||||
const openEth1LogsRc = executeCommandInNewTerminal(wrapCommandInDockerGroup("docker container logs -f rocketpool_eth1"), "eth1 (geth) logs");
|
||||
const openEth1LogsRc = executeCommandInNewTerminal(
|
||||
wrapCommandInDockerGroup('docker container logs -f rocketpool_eth1'),
|
||||
'eth1 (geth) logs',
|
||||
);
|
||||
if (openEth1LogsRc != 0) {
|
||||
console.log("failed to open eth1 logs");
|
||||
console.log('failed to open eth1 logs');
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const openEth2BeaconLogs = () => {
|
||||
const openEth2BeaconLogsRc = executeCommandInNewTerminal(wrapCommandInDockerGroup("docker container logs -f rocketpool_eth2"), "eth2 beacon node (" + getEth2ClientName() + ") logs");
|
||||
const openEth2BeaconLogsRc = executeCommandInNewTerminal(
|
||||
wrapCommandInDockerGroup('docker container logs -f rocketpool_eth2'),
|
||||
'eth2 beacon node (' + getEth2ClientName() + ') logs',
|
||||
);
|
||||
if (openEth2BeaconLogsRc != 0) {
|
||||
console.log("failed to open eth2 beacon logs");
|
||||
console.log('failed to open eth2 beacon logs');
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const openEth2ValidatorLogs = () => {
|
||||
const openEth2ValidatorLogsRc = executeCommandInNewTerminal(wrapCommandInDockerGroup("docker container logs -f rocketpool_validator"), "eth2 validator (" + getEth2ClientName() + ") logs");
|
||||
const openEth2ValidatorLogsRc = executeCommandInNewTerminal(
|
||||
wrapCommandInDockerGroup('docker container logs -f rocketpool_validator'),
|
||||
'eth2 validator (' + getEth2ClientName() + ') logs',
|
||||
);
|
||||
if (openEth2ValidatorLogsRc != 0) {
|
||||
console.log("failed to open eth2 validator logs");
|
||||
console.log('failed to open eth2 validator logs');
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const startNodes = (): Promise<any> => {
|
||||
return executeCommandAsync(wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + " service start"));
|
||||
}
|
||||
return executeCommandAsync(
|
||||
wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + ' service start'),
|
||||
);
|
||||
};
|
||||
|
||||
const stopNodes = (): Promise<any> => {
|
||||
return executeCommandAsync(wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + " service stop -y"));
|
||||
}
|
||||
return executeCommandAsync(
|
||||
wrapCommandInDockerGroup(ROCKET_POOL_EXECUTABLE + ' service stop -y'),
|
||||
);
|
||||
};
|
||||
|
||||
const queryEth1PeerCount = (): number => {
|
||||
const numPeers = executeCommandSyncReturnStdout(wrapCommandInDockerGroup(GETH_PEERS_DOCKER_CMD));
|
||||
const numPeers = executeCommandSyncReturnStdout(
|
||||
wrapCommandInDockerGroup(GETH_PEERS_DOCKER_CMD),
|
||||
);
|
||||
const numPeersNumber = parseInt(numPeers.trim());
|
||||
return isNaN(numPeersNumber) ? 0 : numPeersNumber;
|
||||
}
|
||||
};
|
||||
|
||||
const queryEth1Status = (nodeStatusCallback: NodeStatusCallback) => {
|
||||
dockerContainerStatus("rocketpool_eth1", nodeStatusCallback);
|
||||
}
|
||||
dockerContainerStatus('rocketpool_eth1', nodeStatusCallback);
|
||||
};
|
||||
|
||||
const queryEth1Syncing = (): boolean => {
|
||||
const syncValue = executeCommandSyncReturnStdout(wrapCommandInDockerGroup(GETH_SYNC_STATUS_DOCKER_CMD));
|
||||
return !syncValue.includes("false");
|
||||
}
|
||||
const syncValue = executeCommandSyncReturnStdout(
|
||||
wrapCommandInDockerGroup(GETH_SYNC_STATUS_DOCKER_CMD),
|
||||
);
|
||||
return !syncValue.includes('false');
|
||||
};
|
||||
|
||||
const queryEth2BeaconStatus = (nodeStatusCallback: NodeStatusCallback) => {
|
||||
dockerContainerStatus("rocketpool_eth2", nodeStatusCallback);
|
||||
}
|
||||
dockerContainerStatus('rocketpool_eth2', nodeStatusCallback);
|
||||
};
|
||||
|
||||
const queryEth2ValidatorStatus = (nodeStatusCallback: NodeStatusCallback) => {
|
||||
dockerContainerStatus("rocketpool_validator", nodeStatusCallback);
|
||||
}
|
||||
dockerContainerStatus('rocketpool_validator', nodeStatusCallback);
|
||||
};
|
||||
|
||||
const dockerContainerStatus = async (containerName: string, nodeStatusCallback: NodeStatusCallback) => {
|
||||
const containerId = executeCommandSyncReturnStdout(wrapCommandInDockerGroup("docker ps -q -f name=" + containerName));
|
||||
const dockerContainerStatus = async (
|
||||
containerName: string,
|
||||
nodeStatusCallback: NodeStatusCallback,
|
||||
) => {
|
||||
const containerId = executeCommandSyncReturnStdout(
|
||||
wrapCommandInDockerGroup('docker ps -q -f name=' + containerName),
|
||||
);
|
||||
|
||||
if (containerId.trim()) {
|
||||
nodeStatusCallback(Status.Online);
|
||||
} else {
|
||||
nodeStatusCallback(Status.Offline);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
getEth2ClientName,
|
||||
@@ -204,4 +259,4 @@ export {
|
||||
queryEth1Syncing,
|
||||
queryEth2BeaconStatus,
|
||||
queryEth2ValidatorStatus,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React from 'react';
|
||||
import { shell } from 'electron';
|
||||
import styled from 'styled-components';
|
||||
import { Black, DisabledButton, Heading, MainContent } from '../colors';
|
||||
import Footer from './Footer';
|
||||
import { rem } from 'polished';
|
||||
import { Black, DarkGray, MediumBlue, Gray3 } from '../colors';
|
||||
import { Footer } from './Footer';
|
||||
import { Header } from './typography/Header';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
@@ -12,24 +14,19 @@ const Container = styled.div`
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
const LandingHeader = styled(Header)`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
color: ${Gray3};
|
||||
margin-top: 20;
|
||||
width: 650;
|
||||
flex-grow: 6;
|
||||
`;
|
||||
|
||||
const StyledLink = styled.span`
|
||||
color: ${Heading};
|
||||
color: ${MediumBlue};
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
@@ -46,9 +43,9 @@ const ImportKeysButton = styled.div`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
background-color: ${DisabledButton};
|
||||
background-color: ${DarkGray};
|
||||
padding: 16 24;
|
||||
border-radius: 20px;
|
||||
border-radius: ${rem(20)};
|
||||
text-decoration: none;
|
||||
|
||||
transition: 250ms background-color ease;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Black, Button, ButtonHover } from '../colors';
|
||||
import { Black, LightBlue, LightGreen } from '../colors';
|
||||
|
||||
type FooterProps = {
|
||||
backLink: string;
|
||||
@@ -29,7 +29,7 @@ const StyledButton = styled(Link)`
|
||||
align-items: center;
|
||||
align-self: flex-end;
|
||||
height: 24;
|
||||
background-color: ${Button};
|
||||
background-color: ${LightBlue};
|
||||
padding: 16 24;
|
||||
border-radius: 10%;
|
||||
text-decoration: none;
|
||||
@@ -39,11 +39,11 @@ const StyledButton = styled(Link)`
|
||||
margin: 60;
|
||||
|
||||
&:hover {
|
||||
background-color: ${ButtonHover};
|
||||
background-color: ${LightGreen};
|
||||
}
|
||||
`;
|
||||
|
||||
const Footer = (props: FooterProps) => {
|
||||
export const Footer = (props: FooterProps) => {
|
||||
return (
|
||||
<FooterContainer>
|
||||
{props.backLink ? (
|
||||
@@ -59,5 +59,3 @@ const Footer = (props: FooterProps) => {
|
||||
</FooterContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
|
||||
@@ -6,12 +6,13 @@ import { History } from 'history';
|
||||
import { isRocketPoolInstalled } from '../commands/RocketPool';
|
||||
import {
|
||||
Black,
|
||||
Button,
|
||||
ButtonHover,
|
||||
Heading,
|
||||
MainContent,
|
||||
LightBlue,
|
||||
LightGreen,
|
||||
MediumBlue,
|
||||
Gray3,
|
||||
Red,
|
||||
} from '../colors';
|
||||
import { Header } from './typography/Header';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
@@ -20,17 +21,13 @@ const Container = styled.div`
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
const LandingHeader = styled(Header)`
|
||||
margin-top: 120;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
color: ${Gray3};
|
||||
margin-top: 40;
|
||||
max-width: 650;
|
||||
`;
|
||||
@@ -42,7 +39,7 @@ const StartButton = styled(Link)`
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 24;
|
||||
background-color: ${Button};
|
||||
background-color: ${LightBlue};
|
||||
padding: 16 24;
|
||||
border-radius: 10%;
|
||||
text-decoration: none;
|
||||
@@ -52,12 +49,12 @@ const StartButton = styled(Link)`
|
||||
margin-top: 60;
|
||||
|
||||
&:hover {
|
||||
background-color: ${ButtonHover};
|
||||
background-color: ${LightGreen};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledLink = styled.em`
|
||||
color: ${Heading};
|
||||
color: ${MediumBlue};
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Heading, MainContent } from '../colors';
|
||||
import Footer from './Footer';
|
||||
import { Gray3 } from '../colors';
|
||||
import { Footer } from './Footer';
|
||||
import { Header } from './typography/Header';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
@@ -11,17 +12,12 @@ const Container = styled.div`
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
const LandingHeader = styled(Header)`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
color: ${Gray3};
|
||||
margin-top: 20;
|
||||
width: 650;
|
||||
flex-grow: 6;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import { rem } from 'polished';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { History } from 'history';
|
||||
import { Heading, MainContent } from '../colors';
|
||||
import { Gray3 } from '../colors';
|
||||
import { installAndStartRocketPool } from '../commands/RocketPool';
|
||||
import { Header } from './typography/Header';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
@@ -13,17 +15,12 @@ const Container = styled.div`
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
const LandingHeader = styled(Header)`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
color: ${Gray3};
|
||||
margin-top: 20;
|
||||
width: 650;
|
||||
flex-grow: 6;
|
||||
@@ -41,22 +38,22 @@ const SpinnerContainer = styled.div`
|
||||
`;
|
||||
|
||||
const LoadingSpinner = styled.div`
|
||||
border: 16px solid #f3f3f3; /* Light grey */
|
||||
border-top: 16px solid #3498db; /* Blue */
|
||||
border: ${rem(16)} solid #f3f3f3; /* Light grey */
|
||||
border-top: ${rem(16)} solid #3498db; /* Blue */
|
||||
border-radius: 50%;
|
||||
margin-top: 30px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin-top: ${rem(30)};
|
||||
width: ${rem(120)};
|
||||
height: ${rem(120)};
|
||||
animation: ${rotate} 2s linear infinite;
|
||||
`;
|
||||
|
||||
const LogsContainer = styled.div`
|
||||
height: 250px;
|
||||
height: ${rem(250)};
|
||||
width: 100%;
|
||||
margin-top: 5px;
|
||||
margin-top: ${rem(5)};
|
||||
overflow-y: auto;
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
border-radius: ${rem(5)};
|
||||
border-style: groove;
|
||||
color: black;
|
||||
`;
|
||||
@@ -74,7 +71,7 @@ const LogsListItem = styled.li`
|
||||
|
||||
const LogsContainerAnchor = styled.div``;
|
||||
|
||||
const Installing = ({ history }: { history: History }) => {
|
||||
export const Installing = withRouter(({ history }: { history: History }) => {
|
||||
const anchorRef = useRef(document.createElement('div'));
|
||||
|
||||
const [stdoutText, setStdoutText] = useState(['']);
|
||||
@@ -139,6 +136,4 @@ const Installing = ({ history }: { history: History }) => {
|
||||
</Content>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default withRouter(Installing);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { rem } from 'polished';
|
||||
import {
|
||||
Black,
|
||||
Button,
|
||||
ButtonHover,
|
||||
LightBlue,
|
||||
LightGreen,
|
||||
DarkGray,
|
||||
Gray4,
|
||||
Heading,
|
||||
MainContent,
|
||||
MediumBlue,
|
||||
Gray3,
|
||||
} from '../colors';
|
||||
import { Status, AllStatuses, NodeStatuses } from '../types';
|
||||
import {
|
||||
@@ -31,7 +32,8 @@ import {
|
||||
startNodes,
|
||||
stopNodes,
|
||||
} from '../commands/RocketPool';
|
||||
import Footer from './Footer';
|
||||
import { Footer } from './Footer';
|
||||
import { Header } from './typography/Header';
|
||||
|
||||
const NodeStatus: NodeStatuses = {
|
||||
Online: { code: 0, text: 'Online', character: '\u2B24', color: 'green' },
|
||||
@@ -48,17 +50,12 @@ const Container = styled.div`
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
const LandingHeader = styled(Header)`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
color: ${Gray3};
|
||||
margin-top: 20;
|
||||
width: 650;
|
||||
flex-grow: 6;
|
||||
@@ -67,13 +64,13 @@ const Content = styled.div`
|
||||
const ResultsTable = styled.table`
|
||||
border: 2px solid gray;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
padding: ${rem(15)};
|
||||
text-align: left;
|
||||
color: white;
|
||||
`;
|
||||
|
||||
const StyledLink = styled.span`
|
||||
color: ${Heading};
|
||||
color: ${MediumBlue};
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
@@ -83,15 +80,15 @@ const LogsButton = styled.button`
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${Button};
|
||||
border-radius: 10px;
|
||||
background-color: ${LightBlue};
|
||||
border-radius: ${rem(10)};
|
||||
text-decoration: none;
|
||||
|
||||
transition: 250ms background-color ease;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: ${ButtonHover};
|
||||
background-color: ${LightGreen};
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@@ -104,17 +101,21 @@ const LogsButton = styled.button`
|
||||
// TODO: right after install, while nodes are starting up, this page says everything is "online"
|
||||
// while things are looking for peers. Need to improve that logic.
|
||||
|
||||
const StatusPage = () => {
|
||||
export const StatusPage = () => {
|
||||
const [eth1ContainerStatus, setEth1ContainerStatus] = useState<Status>(
|
||||
Status.Loading,
|
||||
);
|
||||
const [eth1PeerCount, setEth1PeerCount] = useState(0);
|
||||
const [eth1Syncing, setEth1Syncing] = useState(false);
|
||||
const [eth2ClientName, setEth2ClientName] = useState('');
|
||||
const [eth2BeaconContainerStatus, setEth2BeaconContainerStatus] =
|
||||
useState<Status>(Status.Loading);
|
||||
const [eth2ValidatorContainerStatus, setEth2ValidatorContainerStatus] =
|
||||
useState<Status>(Status.Loading);
|
||||
const [
|
||||
eth2BeaconContainerStatus,
|
||||
setEth2BeaconContainerStatus,
|
||||
] = useState<Status>(Status.Loading);
|
||||
const [
|
||||
eth2ValidatorContainerStatus,
|
||||
setEth2ValidatorContainerStatus,
|
||||
] = useState<Status>(Status.Loading);
|
||||
const [ProcessingTotalStatus, setProcessingTotalStatus] = useState<Status>(
|
||||
Status.Online,
|
||||
);
|
||||
@@ -378,5 +379,3 @@ const StatusPage = () => {
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatusPage;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { DarkBlue, Heading, MainContent } from '../colors';
|
||||
import Footer from './Footer';
|
||||
import { rem } from 'polished';
|
||||
import { DarkBlue, Gray3 } from '../colors';
|
||||
import { Footer } from './Footer';
|
||||
import { isRocketPoolInstalled } from '../commands/RocketPool';
|
||||
import { Header } from './typography/Header';
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
@@ -12,17 +14,12 @@ const Container = styled.div`
|
||||
min-height: 100vh;
|
||||
`;
|
||||
|
||||
const LandingHeader = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
margin-top: 50;
|
||||
color: ${Heading};
|
||||
max-width: 550;
|
||||
const LandingHeader = styled(Header)`
|
||||
flex-grow: 1;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
color: ${MainContent};
|
||||
color: ${Gray3};
|
||||
margin-top: 20;
|
||||
width: 650;
|
||||
flex-grow: 6;
|
||||
@@ -38,7 +35,7 @@ const Advanced = styled.div`
|
||||
const ResultsTable = styled.table`
|
||||
border: 2px solid gray;
|
||||
width: 75%;
|
||||
padding: 15px;
|
||||
padding: ${rem(15)};
|
||||
text-align: left;
|
||||
color: white;
|
||||
`;
|
||||
|
||||
15
src/react/components/typography/Header.stories.tsx
Normal file
15
src/react/components/typography/Header.stories.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
|
||||
import { Header } from './Header';
|
||||
|
||||
export default {
|
||||
title: 'Example/HeaderNew',
|
||||
component: Header,
|
||||
} as ComponentMeta<typeof Header>;
|
||||
|
||||
const Template: ComponentStory<typeof Header> = () => {
|
||||
return <Header>Test Header Text</Header>;
|
||||
};
|
||||
|
||||
export const LoggedIn = Template.bind({});
|
||||
10
src/react/components/typography/Header.tsx
Normal file
10
src/react/components/typography/Header.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import styled from 'styled-components';
|
||||
import { MediumBlue } from '../../colors';
|
||||
|
||||
export const Header = styled.div`
|
||||
font-weight: 700;
|
||||
font-size: 35;
|
||||
color: ${MediumBlue};
|
||||
max-width: 550;
|
||||
margin-top: 50;
|
||||
`;
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
|
||||
import App from "./App";
|
||||
import { App } from './App';
|
||||
|
||||
// We find our app DOM element as before
|
||||
const app = document.getElementById("app");
|
||||
const app = document.getElementById('app');
|
||||
|
||||
// Finally, we render our top-level component to the actual DOM.
|
||||
ReactDOM.render(<App />, app);
|
||||
|
||||
Reference in New Issue
Block a user