Skip to content
This repository has been archived by the owner on Jan 31, 2023. It is now read-only.

Adds basic layout #17

Merged
merged 5 commits into from
Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion web/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@
}
}
],
"unicorn/prevent-abbreviations": [
"error",
{
"ignore": [".*[pP]rops"]
}
],
"unicorn/no-nested-ternary": ["error"]
},
"settings": {
Expand All @@ -144,7 +150,7 @@
{
"files": ["src/**/*.ts?(x)"],
"parserOptions": {
"project": ["./web/tsconfig.json"]
"project": ["./tsconfig.json"]
madsnedergaard marked this conversation as resolved.
Show resolved Hide resolved
}
},
{
Expand Down
1 change: 1 addition & 0 deletions web/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"files.watcherExclude": {
"**/dist": true
},
"eslint.workingDirectories": [{ "mode": "auto" }],
"javascript.format.enable": false,
"typescript.disableAutomaticTypeAcquisition": true,
"typescript.tsdk": "node_modules/typescript/lib",
Expand Down
2 changes: 1 addition & 1 deletion web/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en" class="text-gray-900 antialiased dark:bg-gray-800 dark:text-white">
<html lang="en" class="antialiased">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/favicon.png" />
Expand Down
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"react-dom": "18.2.0",
"react-map-gl": "^7.0.19",
"react-router-dom": "6.3.0",
"tailwindcss-classnames": "^3.0.6",
madsnedergaard marked this conversation as resolved.
Show resolved Hide resolved
"tiny-invariant": "^1.3.1"
},
"devDependencies": {
Expand Down Expand Up @@ -81,7 +82,6 @@
"prettier-plugin-tailwindcss": "0.1.13",
"start-server-and-test": "1.14.0",
"tailwindcss": "3.1.8",
"tailwindcss-classnames": "^3.0.6",
"typescript": "4.8.4",
"vite": "3.1.8",
"vite-plugin-pwa": "0.13.1",
Expand Down
26 changes: 26 additions & 0 deletions web/public/electricity-maps-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 13 additions & 5 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import LoadingOrError from 'components/LoadingOrError';
import LeftPanel from 'features/panels/LeftPanel';
import ErrorBoundary from 'features/error-boundary/ErrorBoundary';
import Header from 'features/header/Header';
import type { ReactElement } from 'react';
import { lazy, Suspense } from 'react';

const Map = lazy(async () => import('features/map/Map'));
const LeftPanel = lazy(async () => import('features/panels/LeftPanel'));

export default function App(): ReactElement {
return (
<Suspense fallback={<LoadingOrError />}>
<LeftPanel />
<Map />
<Suspense fallback={<div />}>
<div className="h-full">
<Header />
<div className="flex h-screen">
<ErrorBoundary>
madsnedergaard marked this conversation as resolved.
Show resolved Hide resolved
<LeftPanel />
<Map />
</ErrorBoundary>
</div>
</div>
</Suspense>
);
}
57 changes: 57 additions & 0 deletions web/src/features/error-boundary/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Component, ErrorInfo, ReactNode } from 'react';

interface Props {
children?: ReactNode;
}

interface State {
hasError: boolean;
error: Error | undefined;
}

class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
error: undefined,
};

public static getDerivedStateFromError(error: Error): State {
// Update state so the next render will show the fallback UI.
return { hasError: true, error: error };
}

public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
// TODO: Send this to Sentry
console.error('Uncaught error:', error, errorInfo);
}

public render() {
if (this.state.hasError) {
const url = window.location.href;
return (
<div className="flex w-full flex-col items-center justify-center">
<h1>Oh no, something went wrong...</h1>
<p>
Please let us know <a href="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/electricityMaps/electricitymaps-contrib">on Github</a> so we
can fix this!
</p>
<pre className="rounded-lg bg-gray-300 p-2 text-xs">
Error message: {this.state.error?.message}
<br />
Url: {url}
</pre>
<a
href="/map"
className="mt-4 cursor-pointer rounded-lg border border-gray-200 bg-gray-100 p-2 text-base text-black"
>
Back to map
</a>
</div>
);
}

return this.props.children;
}
}

export default ErrorBoundary;
52 changes: 52 additions & 0 deletions web/src/features/header/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as NavigationMenu from '@radix-ui/react-navigation-menu';
import { classnames } from 'tailwindcss-classnames';

interface MenuLinkProps {
href: string;
children: React.ReactNode;
active?: boolean;
}
function MenuLink({ children, href, active }: MenuLinkProps): JSX.Element {
return (
<div className="relative flex py-2 ">
<NavigationMenu.Item asChild className="rounded-md transition-colors hover:bg-zinc-100">
<NavigationMenu.Link
active={active}
href={href}
className={classnames('px-2 py-2 text-sm', active && 'font-bold')}
>
{children}
{active && <div className="absolute left-0 bottom-0 h-[2px] w-full bg-green-500"></div>}
</NavigationMenu.Link>
</NavigationMenu.Item>
</div>
);
}

export default function Header(): JSX.Element {
return (
<div className="flex items-center justify-between pl-4 pr-8 shadow-md">
<img src="/electricity-maps-logo.svg" alt="" className="w-100 h-10" />

<NavigationMenu.Root>
<NavigationMenu.List className="flex space-x-2">
<MenuLink href="/" active>
Live
</MenuLink>
<MenuLink href="https://electricitymaps.com/open-source/?utm_source=app.electricitymaps.com&utm_medium=referral">
We&apos;re hiring!
</MenuLink>
<MenuLink href="https://electricitymaps.com/open-source/?utm_source=app.electricitymaps.com&utm_medium=referral">
Open Source
</MenuLink>
<MenuLink href="https://electricitymaps.com/blog/?utm_source=app.electricitymaps.com&utm_medium=referral">
Blog
</MenuLink>
<MenuLink href="https://electricitymaps.com?utm_source=app.electricitymaps.com&utm_medium=referral">
Get our data
</MenuLink>
</NavigationMenu.List>
</NavigationMenu.Root>
</div>
);
}
31 changes: 15 additions & 16 deletions web/src/features/panels/LeftPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BrowserRouter, Navigate, Route, Routes, useParams } from 'react-router-dom';
import { Navigate, Route, Routes, useParams } from 'react-router-dom';
import ZoneDetails from './Zone/ZoneDetails';

function ValidZoneIdGuardWrapper({ children }: { children: JSX.Element }): JSX.Element {
Expand All @@ -12,21 +12,20 @@ function ValidZoneIdGuardWrapper({ children }: { children: JSX.Element }): JSX.E

export default function LeftPanel(): JSX.Element {
return (
<div className="left-panel">
<BrowserRouter>
<Routes>
<Route path="/" element={<p>Ranking</p>} />
<Route path="/map" element={<p>Ranking</p>} />
<Route
path="/zone/:zoneId"
element={
<ValidZoneIdGuardWrapper>
<ZoneDetails />
</ValidZoneIdGuardWrapper>
}
/>
</Routes>
</BrowserRouter>
<div className=" flex w-4/12 bg-zinc-200 shadow-lg">
<Routes>
<Route path="/" element={<p>Ranking Panel</p>} />
<Route
path="/zone/:zoneId"
element={
<ValidZoneIdGuardWrapper>
<ZoneDetails />
</ValidZoneIdGuardWrapper>
}
/>
{/* Alternative: add /map here and have a NotFound component for anything else*/}
<Route path="*" element={<p>Ranking Panel</p>} />
</Routes>
</div>
);
}
6 changes: 6 additions & 0 deletions web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,9 @@
src: url('/fonts/Inter-Bold.woff2') format('woff2');
}
}

html,
body,
#root {
@apply h-full;
}
13 changes: 9 additions & 4 deletions web/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { REFETCH_INTERVAL_MS } from 'api/helpers';
import App from 'App';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter } from 'react-router-dom';
import { createConsoleGreeting } from 'utils/createConsoleGreeting';
import { registerSW } from 'virtual:pwa-register';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import './index.css';
import { REFETCH_INTERVAL_MS } from 'api/helpers';

registerSW();
createConsoleGreeting();

const MAX_RETRIES = 1;
const queryClient = new QueryClient({
Expand All @@ -25,7 +28,9 @@ if (container) {
root.render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
<BrowserRouter>
<App />
</BrowserRouter>
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</StrictMode>
Expand Down
15 changes: 15 additions & 0 deletions web/src/utils/createConsoleGreeting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Send a greeting message to the console for curious people
export function createConsoleGreeting() {
console.info(
madsnedergaard marked this conversation as resolved.
Show resolved Hide resolved
`路 路 路
路 蠠 路 %cWelcome to Electricity Maps!%c
路 路 路
馃尡 %cReady to work on fixing climate change full-time?
https://electricitymaps.com/jobs
馃悪 Got comments or want to contribute?
https://github.com/electricityMaps/electricitymaps-contrib`,
'color: green; font-weight: bold',
'color: inherit',
'color: #666; font-style: italic'
madsnedergaard marked this conversation as resolved.
Show resolved Hide resolved
);
}