Skip to content

Commit

Permalink
used apollo suspense query to implement loading modal
Browse files Browse the repository at this point in the history
  • Loading branch information
amorey committed Nov 24, 2024
1 parent 7303fee commit 5362a5b
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 40 deletions.
1 change: 1 addition & 0 deletions dashboard-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"react": "^18.3.1",
"react-day-picker": "^8.10.1",
"react-dom": "^18.3.1",
"react-error-boundary": "^4.1.2",
"react-hot-toast": "^2.4.1",
"react-timeago": "^7.2.0",
"react-virtualized-auto-sizer": "^1.0.24",
Expand Down
13 changes: 13 additions & 0 deletions dashboard-ui/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 34 additions & 2 deletions dashboard-ui/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
// limitations under the License.

import { ApolloProvider } from '@apollo/client';
import React from 'react';
import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import { ErrorBoundary } from 'react-error-boundary';
import { createBrowserRouter, createRoutesFromElements, RouterProvider } from 'react-router-dom';

import Spinner from '@kubetail/ui/elements/Spinner';

import { routes } from './routes';
import client from '@/apollo-client';
import { SessionProvider } from '@/lib/auth';
Expand All @@ -27,12 +30,41 @@ import './index.css';

const router = createBrowserRouter(createRoutesFromElements(routes), { basename: getBasename() });

function fallbackRenderError({ error }: { error: Error }) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre className="text-red-500">{error.message}</pre>
</div>
);
}

const LoadingModal = () => (
<div className="relative z-10" role="dialog">
<div className="fixed inset-0 bg-chrome-500 bg-opacity-75" />
<div className="fixed inset-0 z-10 w-screen">
<div className="flex min-h-full items-center justify-center p-0 text-center">
<div className="relative transform overflow-hidden rounded-lg bg-background my-8 p-6 text-left shadow-xl">
<div className="flex items-center space-x-2">
<div>Connecting...</div>
<Spinner size="sm" />
</div>
</div>
</div>
</div>
</div>
);

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ApolloProvider client={client}>
<SessionProvider>
<ThemeProvider>
<RouterProvider router={router} />
<ErrorBoundary fallbackRender={fallbackRenderError}>
<Suspense fallback={<LoadingModal />}>
<RouterProvider router={router} />
</Suspense>
</ErrorBoundary>
</ThemeProvider>
</SessionProvider>
</ApolloProvider>
Expand Down
23 changes: 2 additions & 21 deletions dashboard-ui/src/pages/_root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { useQuery } from '@apollo/client';
import { useSuspenseQuery } from '@apollo/client';
import { XCircleIcon } from '@heroicons/react/24/outline';
import { useEffect, useState } from 'react';
import toastlib, { useToaster, resolveValue } from 'react-hot-toast';
import type { Toast } from 'react-hot-toast';
import { Outlet } from 'react-router-dom';

import Button from '@kubetail/ui/elements/Button';
import Spinner from '@kubetail/ui/elements/Spinner';

import Modal from '@/components/elements/Modal';
import * as ops from '@/lib/graphql/ops';
Expand Down Expand Up @@ -95,22 +94,6 @@ const CustomToaster = () => {
);
};

const LoadingModal = () => (
<div className="relative z-10" role="dialog">
<div className="fixed inset-0 bg-chrome-500 bg-opacity-75" />
<div className="fixed inset-0 z-10 w-screen">
<div className="flex min-h-full items-center justify-center p-0 text-center">
<div className="relative transform overflow-hidden rounded-lg bg-background my-8 p-6 text-left shadow-xl">
<div className="flex items-center space-x-2">
<div>Connecting to cluster...</div>
<Spinner size="sm" />
</div>
</div>
</div>
</div>
</div>
);

export default function Root() {
// update favicon location
useEffect(() => {
Expand All @@ -119,12 +102,10 @@ export default function Root() {
el.setAttribute('href', joinPaths(getBasename(), '/favicon.ico'));
}, []);

/*
const { loading } = useQuery(ops.READY_WAIT, {
useSuspenseQuery(ops.READY_WAIT, {
fetchPolicy: 'no-cache',
onError: console.log,
});
*/

return (
<>
Expand Down
63 changes: 49 additions & 14 deletions dashboard-ui/src/routes.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { render } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { render, waitFor } from '@testing-library/react';
import { Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { MemoryRouter, Routes } from 'react-router-dom';

import * as ops from '@/lib/graphql/ops';
import { routes } from './routes';

vi.mock('@/pages/home', () => ({
Expand All @@ -33,34 +38,64 @@ vi.mock('@/pages/auth/logout', () => ({
default: () => <div>Auth-Logout</div>,
}));

const mocks: MockedResponse[] = [
{
request: {
query: ops.READY_WAIT,
},
result: {
data: {
readyWait: true,
},
},
},
];

const renderPage = (path: string) => (
render(
<MemoryRouter initialEntries={[path]}>
<Routes>
{routes}
</Routes>
</MemoryRouter>,
<MockedProvider
mocks={mocks}
addTypename={false}
>
<ErrorBoundary fallback={<div>error</div>}>
<Suspense fallback={<div>loading...</div>}>
<MemoryRouter initialEntries={[path]}>
<Routes>
{routes}
</Routes>
</MemoryRouter>
</Suspense>
</ErrorBoundary>
</MockedProvider>,
)
);

describe('route tests', () => {
it('/', () => {
it('/', async () => {
const { getByText } = renderPage('/');
expect(getByText('Home')).toBeInTheDocument();
await waitFor(() => {
expect(getByText('Home')).toBeInTheDocument();
});
});

it('/console', () => {
it('/console', async () => {
const { getByText } = renderPage('/console');
expect(getByText('Console')).toBeInTheDocument();
await waitFor(() => {
expect(getByText('Console')).toBeInTheDocument();
});
});

it('/auth/login', () => {
it('/auth/login', async () => {
const { getByText } = renderPage('/auth/login');
expect(getByText('Auth-Login')).toBeInTheDocument();
await waitFor(() => {
expect(getByText('Auth-Login')).toBeInTheDocument();
});
});

it('/auth/logout', () => {
it('/auth/logout', async () => {
const { getByText } = renderPage('/auth/logout');
expect(getByText('Auth-Logout')).toBeInTheDocument();
await waitFor(() => {
expect(getByText('Auth-Logout')).toBeInTheDocument();
});
});
});
6 changes: 3 additions & 3 deletions dashboard-ui/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
/* import paths */
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
"@/*": [
"./src/*"
]
},

/* misc */
Expand Down

0 comments on commit 5362a5b

Please sign in to comment.