mirror of
https://github.com/gabehf/Koito.git
synced 2026-03-07 13:38:15 -08:00
adding functions for loading indicator
This commit is contained in:
parent
0708cfbc02
commit
48abcc33ff
11 changed files with 8059 additions and 829 deletions
13
client/app/components/LoadingIndicator.tsx
Normal file
13
client/app/components/LoadingIndicator.tsx
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
export default function LoadingIndicator({ label = "Loading" }: { label?: string }) {
|
||||
return (
|
||||
<div
|
||||
role="status"
|
||||
aria-label={label}
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40"
|
||||
>
|
||||
// change the size of the indicater and color
|
||||
<div className="h-12 w-12 animate-spin rounded-full border-4 border-white border-t-transparent" />
|
||||
<span className="sr-only">{label}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
61
client/app/providers/AppProvider.test.tsx
Normal file
61
client/app/providers/AppProvider.test.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import React from "react";
|
||||
|
||||
vi.mock("api/api", () => ({
|
||||
getCfg: vi.fn(),
|
||||
}));
|
||||
|
||||
function deferred<T>() {
|
||||
let resolve!: (value: T) => void;
|
||||
const promise = new Promise<T>((res) => {
|
||||
resolve = res;
|
||||
});
|
||||
return { promise, resolve };
|
||||
}
|
||||
|
||||
describe("AppProvider loading indicator", () => {
|
||||
beforeEach(() => {vi.resetAllMocks();}
|
||||
);
|
||||
|
||||
afterEach(() => {vi.unstubAllGlobals();});
|
||||
|
||||
it("Loading indicator while fetching", async () => {
|
||||
const userDef = deferred<any>();
|
||||
const cfgDef = deferred<any>();
|
||||
|
||||
vi.stubGlobal("fetch", vi.fn((input: any) => {
|
||||
const url = String(input);
|
||||
if (url.includes("/apis/web/v1/user/me")) {
|
||||
return userDef.promise.then((data) => ({
|
||||
ok: true,
|
||||
json: async () => data,
|
||||
})) as any;
|
||||
}
|
||||
throw new Error(`Unexpected fetch: ${url}`);
|
||||
}));
|
||||
|
||||
const { getCfg } = await import("api/api");
|
||||
(getCfg as any).mockReturnValue(cfgDef.promise);
|
||||
|
||||
const { AppProvider } = await import("./AppProvider");
|
||||
|
||||
render(
|
||||
<AppProvider>
|
||||
<div>APP CONTENT</div>
|
||||
</AppProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByRole("status", { name: /loading/i })).toBeInTheDocument();
|
||||
expect(screen.queryByText("APP CONTENT")).not.toBeInTheDocument();
|
||||
|
||||
userDef.resolve({ username: "test-user" });
|
||||
cfgDef.resolve({ default_theme: "testing" });
|
||||
|
||||
expect(await screen.findByText("APP CONTENT")).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("status", { name: /loading/i })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { getCfg, type User } from "api/api";
|
||||
import { createContext, useContext, useEffect, useState } from "react";
|
||||
import LoadingIndicator from "~/components/LoadingIndicator";
|
||||
|
||||
interface AppContextType {
|
||||
user: User | null | undefined;
|
||||
|
|
@ -26,8 +27,7 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
|||
const [defaultTheme, setDefaultTheme] = useState<string | undefined>(
|
||||
undefined
|
||||
);
|
||||
const [configurableHomeActivity, setConfigurableHomeActivity] =
|
||||
useState<boolean>(false);
|
||||
const [configurableHomeActivity, setConfigurableHomeActivity] = useState<boolean>(false);
|
||||
const [homeItems, setHomeItems] = useState<number>(0);
|
||||
|
||||
const setUsername = (value: string) => {
|
||||
|
|
@ -53,14 +53,15 @@ export const AppProvider = ({ children }: { children: React.ReactNode }) => {
|
|||
if (cfg.default_theme !== "") {
|
||||
setDefaultTheme(cfg.default_theme);
|
||||
} else {
|
||||
setDefaultTheme("yuu");
|
||||
setDefaultTheme("Testing");
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Block rendering the app until config is loaded
|
||||
if (user === undefined || defaultTheme === undefined) {
|
||||
return null;
|
||||
const isLoading = user === undefined || defaultTheme === undefined;
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingIndicator label="Loading app" />;
|
||||
}
|
||||
|
||||
const contextValue: AppContextType = {
|
||||
|
|
|
|||
6981
client/package-lock.json
generated
Normal file
6981
client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -7,7 +7,9 @@
|
|||
"build": "react-router build",
|
||||
"dev": "react-router dev",
|
||||
"start": "react-router-serve ./build/server/index.js",
|
||||
"typecheck": "react-router typegen && tsc"
|
||||
"typecheck": "react-router typegen && tsc",
|
||||
"test": "vitest"
|
||||
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-tabs": "^1.1.12",
|
||||
|
|
|
|||
1
client/test/setup.ts
Normal file
1
client/test/setup.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
import "@testing-library/jest-dom/vitest";
|
||||
|
|
@ -15,7 +15,8 @@
|
|||
"rootDirs": [".", "./.react-router/types"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
"~/*": ["./app/*"],
|
||||
"api/*": ["./api/*"]
|
||||
},
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
|
|
@ -24,4 +25,4 @@
|
|||
"skipLibCheck": true,
|
||||
"strict": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import path from "node:path";
|
||||
import { reactRouter } from "@react-router/dev/vite";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { defineConfig } from "vite";
|
||||
|
|
@ -7,7 +8,12 @@ import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'
|
|||
const isDocker = process.env.BUILD_TARGET === 'docker';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), reactRouter(), tsconfigPaths(), vanillaExtractPlugin()],
|
||||
plugins: [
|
||||
tailwindcss(),
|
||||
reactRouter(),
|
||||
tsconfigPaths({ projects: ["./tsconfig.json"] }),
|
||||
vanillaExtractPlugin(),
|
||||
],
|
||||
server: {
|
||||
proxy: {
|
||||
'/apis': {
|
||||
|
|
@ -24,7 +30,8 @@ export default defineConfig({
|
|||
alias: {
|
||||
...(isDocker
|
||||
? { 'react-dom/server': 'react-dom/server.node' }
|
||||
: {}),
|
||||
},
|
||||
},
|
||||
: {}),
|
||||
api: path.resolve(__dirname, "api"),
|
||||
},
|
||||
},
|
||||
});
|
||||
16
client/vitest.config.ts
Normal file
16
client/vitest.config.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { defineConfig } from "vitest/config";
|
||||
import tsconfigPaths from "vite-tsconfig-paths";
|
||||
import path from "node:path";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tsconfigPaths({ projects: ["./tsconfig.json"] })],
|
||||
resolve: {
|
||||
alias: {
|
||||
api: path.resolve(__dirname, "api"),
|
||||
},
|
||||
},
|
||||
test: {
|
||||
environment: "jsdom",
|
||||
setupFiles: ["./test/setup.ts"],
|
||||
},
|
||||
});
|
||||
1777
client/yarn.lock
1777
client/yarn.lock
File diff suppressed because it is too large
Load diff
2
go.mod
2
go.mod
|
|
@ -1,6 +1,6 @@
|
|||
module github.com/gabehf/koito
|
||||
|
||||
go 1.24.2
|
||||
go 1.24
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.2.1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue