Peter Makowski has proposed merging maas-site-manager:feat-table-component-MAASENG-1387 into maas-site-manager:main.
Requested reviews: MAAS Committers (maas-committers) For more details, see: https://code.launchpad.net/~maas-committers/maas-site-manager/+git/site-manager/+merge/437995 -- Your team MAAS Committers is requested to review the proposed merge of maas-site-manager:feat-table-component-MAASENG-1387 into maas-site-manager:main.
diff --git a/.env b/.env index 22cb9fd..797df3b 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ -UI_PORT=8405 \ No newline at end of file +VITE_UI_PORT=8405 +VITE_API_URL=http://localhost:8000 diff --git a/frontend/package.json b/frontend/package.json index cfa7539..c70b8d3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,12 @@ "preview": "vite preview" }, "dependencies": { + "@canonical/react-components": "0.38.0", + "@tanstack/react-table": "8.7.9", + "axios": "1.3.4", + "date-fns": "2.29.3", + "date-fns-tz": "2.0.0", + "lodash": "4.17.21", "react": "18.2.0", "react-dom": "18.2.0", "react-query": "3.39.3", @@ -18,15 +24,19 @@ "vanilla-framework": "3.11.0" }, "devDependencies": { - "@playwright/test": "^1.31.1", + "@playwright/test": "1.31.1", "@testing-library/jest-dom": "5.16.5", "@testing-library/react": "13.4.0", + "@types/axios": "0.14.0", + "@types/lodash": "4.14.191", "@types/react": "18.0.27", "@types/react-dom": "18.0.10", "@vitejs/plugin-react-swc": "3.0.0", - "dotenv": "^16.0.3", + "dotenv": "16.0.3", + "mockdate": "3.0.5", "msw": "1.0.1", "sass": "1.58.1", + "timezone-mock": "1.3.6", "typescript": "4.9.3", "vite": "4.1.0", "vitest": "0.28.5" diff --git a/frontend/setupTests.js b/frontend/setupTests.ts similarity index 90% rename from frontend/setupTests.js rename to frontend/setupTests.ts index 45657de..d025d9d 100644 --- a/frontend/setupTests.js +++ b/frontend/setupTests.ts @@ -1,7 +1,10 @@ +import dotenv from "dotenv"; import { expect, afterEach } from "vitest"; import { cleanup } from "@testing-library/react"; import matchers from "@testing-library/jest-dom/matchers"; +dotenv.config({ path: "../.env" }); + // extends Vitest's expect method with methods from react-testing-library expect.extend(matchers); diff --git a/frontend/src/App.scss b/frontend/src/App.scss index 6049fbf..9a5b6a7 100644 --- a/frontend/src/App.scss +++ b/frontend/src/App.scss @@ -1,5 +1,6 @@ @import "node_modules/vanilla-framework"; @include vf-base; +@include vanilla; // @include vf-p-table-sortable; // @include vf-p-table-expanding; @include vf-p-grid; diff --git a/frontend/src/api/api.ts b/frontend/src/api/api.ts new file mode 100644 index 0000000..9b40510 --- /dev/null +++ b/frontend/src/api/api.ts @@ -0,0 +1,7 @@ +import axios from "axios"; + +const api = axios.create({ + baseURL: import.meta.env.VITE_API_URL, +}); + +export default api; diff --git a/frontend/src/api/handlers.ts b/frontend/src/api/handlers.ts new file mode 100644 index 0000000..8877ca6 --- /dev/null +++ b/frontend/src/api/handlers.ts @@ -0,0 +1,11 @@ +import api from "./api"; +import urls from "./urls"; + +export const getSites = async () => { + try { + const response = await api.get(urls.sites); + return response.data; + } catch (error) { + console.error(error); + } +}; diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts new file mode 100644 index 0000000..9e19686 --- /dev/null +++ b/frontend/src/api/index.ts @@ -0,0 +1 @@ +export { default } from "./api"; diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts new file mode 100644 index 0000000..c8495ac --- /dev/null +++ b/frontend/src/api/types.ts @@ -0,0 +1,26 @@ +export type Site = { + name: string; + url: string; // <full URL including protocol>, + connection: "stable" | "stale" | "lost"; + last_seen: string; // <ISO 8601 date>, + address: { + countrycode: string; // <alpha2 country code>, + city: string; + zip: string; + street: string; + }; + timezone: string; // <three letter abbreviation>, + stats: { + machines: number; + occupied_machines: number; + ready_machines: number; + error_machines: number; + }; +}; + +export type Sites = { + items: Site[]; + total: number; + page: number; + size: number; +}; diff --git a/frontend/src/api/urls.ts b/frontend/src/api/urls.ts new file mode 100644 index 0000000..0a3217a --- /dev/null +++ b/frontend/src/api/urls.ts @@ -0,0 +1,7 @@ +import { getApiUrl } from "./utils"; + +const urls = { + sites: getApiUrl("/api/sites"), +}; + +export default urls; diff --git a/frontend/src/api/utils.ts b/frontend/src/api/utils.ts new file mode 100644 index 0000000..7386f0f --- /dev/null +++ b/frontend/src/api/utils.ts @@ -0,0 +1,3 @@ +export const getApiUrl = (path: string) => { + return new URL(path, import.meta.env.VITE_API_URL).toString(); +}; diff --git a/frontend/src/components/MainLayout/MainLayout.scss b/frontend/src/components/MainLayout/MainLayout.scss new file mode 100644 index 0000000..12ae5c8 --- /dev/null +++ b/frontend/src/components/MainLayout/MainLayout.scss @@ -0,0 +1,3 @@ +.l-main.is-maas-site-manager { + margin-top: 1.5rem; +} diff --git a/frontend/src/components/MainLayout/MainLayout.tsx b/frontend/src/components/MainLayout/MainLayout.tsx index 9b55505..8218511 100644 --- a/frontend/src/components/MainLayout/MainLayout.tsx +++ b/frontend/src/components/MainLayout/MainLayout.tsx @@ -1,11 +1,12 @@ import { Outlet } from "react-router-dom"; +import "./MainLayout.scss"; const MainLayout = () => ( <div className="l-application"> - <main className="l-main"> + <main className="l-main is-maas-site-manager"> <div className="row"> <div className="col-12"> - <h1>MAAS Site Manager</h1> + <h1 className="u-hide">MAAS Site Manager</h1> <Outlet /> </div> </div> diff --git a/frontend/src/components/SitesList/SitesList.scss b/frontend/src/components/SitesList/SitesList.scss deleted file mode 100644 index cad2f95..0000000 --- a/frontend/src/components/SitesList/SitesList.scss +++ /dev/null @@ -1,3 +0,0 @@ -table { - table-layout: auto; -} diff --git a/frontend/src/components/SitesList/SitesList.test.tsx b/frontend/src/components/SitesList/SitesList.test.tsx index e23e81c..75cf9c5 100644 --- a/frontend/src/components/SitesList/SitesList.test.tsx +++ b/frontend/src/components/SitesList/SitesList.test.tsx @@ -1,5 +1,21 @@ import SitesList from "./SitesList"; -import { render, screen } from "../../test-utils"; +import { render, screen, waitFor, within } from "../../test-utils"; +import { createMockGetServer } from "../../mocks/server"; +import { sites } from "../../mocks/factories"; +import urls from "../../api/urls"; + +const sitesData = sites(); +const mockServer = createMockGetServer(urls.sites, sitesData); + +beforeAll(() => { + mockServer.listen(); +}); +afterEach(() => { + mockServer.resetHandlers(); +}); +afterAll(() => { + mockServer.close(); +}); it("renders header", () => { render(<SitesList />); @@ -8,3 +24,30 @@ it("renders header", () => { screen.getByRole("heading", { name: /MAAS Regions/i }) ).toBeInTheDocument(); }); + +it("displays loading text", () => { + render(<SitesList />); + + expect(screen.getByText(/loading/i)).toBeInTheDocument(); +}); + +it("displays populated sites table", async () => { + const { items } = sitesData; + render(<SitesList />); + + await waitFor(() => + expect(screen.getByRole("table", { name: /sites/i })).toBeInTheDocument() + ); + + expect(screen.getAllByRole("rowgroup")).toHaveLength(2); + expect( + screen.getByRole("heading", { name: /2 MAAS Regions/i }) + ).toBeInTheDocument(); + const tableBody = screen.getAllByRole("rowgroup")[1]; + expect(within(tableBody).getAllByRole("row")).toHaveLength(items.length); + within(tableBody) + .getAllByRole("row") + .forEach((row, i) => + expect(row).toHaveTextContent(new RegExp(items[i].name, "i")) + ); +}); diff --git a/frontend/src/components/SitesList/SitesList.tsx b/frontend/src/components/SitesList/SitesList.tsx index 9a49bd0..571a440 100644 --- a/frontend/src/components/SitesList/SitesList.tsx +++ b/frontend/src/components/SitesList/SitesList.tsx @@ -1,54 +1,18 @@ -import { useQuery } from "react-query"; -import SiteRow from "./components/SiteRow"; -import "./SitesList.scss"; -import { Sites } from "./types"; +import { useSitesQuery } from "../../hooks/api"; +import SitesTable from "./components/SitesTable"; const SitesList = () => { - const query = useQuery<Sites>("/api/sites", async function () { - const response = await fetch("/api/sites"); - if (!response.ok) { - throw new Error("Network response was not ok"); - } - const responseJson = await response.json(); - return responseJson; - }); + const query = useSitesQuery(); return ( <div> - <h2>{query?.data?.items?.length || ""} MAAS Regions</h2> - <table> - <thead> - <tr> - <th> - <div>MAAS region alias</div> - <div>URL</div> - </th> - <th> - <div>connection</div> - <div>last seen</div> - </th> - <th> - <div>country</div> - <div>street, city, zip</div> - </th> - <th> - <div>local time</div> - <div>timezone</div> - </th> - <th> - <div>total number of machines</div> - <div>machines per aggregated status</div> - </th> - </tr> - </thead> - <tbody> - {query.data - ? query.data.items.map((site) => ( - <SiteRow key={site.url} site={site} /> - )) - : null} - </tbody> - </table> + <h2 className="p-heading--4"> + {query?.data?.items?.length || ""} MAAS Regions + </h2> + {query.isLoading && !query.isFetchedAfterMount ? "Loading..." : null} + {query.isFetchedAfterMount && query.data ? ( + <SitesTable data={query.data.items} /> + ) : null} </div> ); }; diff --git a/frontend/src/components/SitesList/components/SiteRow.tsx b/frontend/src/components/SitesList/components/SiteRow.tsx deleted file mode 100644 index 22ed089..0000000 --- a/frontend/src/components/SitesList/components/SiteRow.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Site } from "../types"; - -const SiteRow = ({ site }: { site: Site }) => { - return ( - <tr> - <td> - <div>{site.name}</div> - <div> - <a href={site.url}>{site.url}</a> - </div> - </td> - <td> - <div>{site.connection}</div> - <div>{site.last_seen}</div> - </td> - <td> - <div>{site.address.countrycode}</div> - <div> - {site.address.street}, {site.address.city}, {site.address.zip} - </div> - </td> - <td> - <div>11:00 (local time)</div> - <div>{site.timezone}</div> - </td> - <td> - <div>{site.stats.machines}</div> - <div> - Ready: {site.stats.ready_machines}, Occupied: - {site.stats.occupied_machines}, Error: {site.stats.error_machines} - </div> - </td> - </tr> - ); -}; - -export default SiteRow; diff --git a/frontend/src/components/SitesList/components/SitesTable.test.tsx b/frontend/src/components/SitesList/components/SitesTable.test.tsx new file mode 100644 index 0000000..4fd2a23 --- /dev/null +++ b/frontend/src/components/SitesList/components/SitesTable.test.tsx @@ -0,0 +1,49 @@ +import SitesTable from "./SitesTable"; +import { render, screen, within } from "../../../test-utils"; +import { sites, site } from "../../../mocks/factories"; +import { Site } from "../../../api/types"; +import MockDate from "mockdate"; +import timezoneMock from "timezone-mock"; +import { vi } from "vitest"; + +beforeEach(() => { + vi.useFakeTimers(); + timezoneMock.register("Etc/GMT"); +}); + +afterEach(() => { + timezoneMock.unregister(); + vi.useRealTimers(); +}); + +it("displays an empty sites table", () => { + render(<SitesTable data={[]} />); + + expect(screen.getByRole("table", { name: /sites/i })).toBeInTheDocument(); +}); + +it("displays rows with details for each site", () => { + const items = sites().items as Site[]; + render(<SitesTable data={items} />); + + expect(screen.getByRole("table", { name: /sites/i })).toBeInTheDocument(); + + const tableBody = screen.getAllByRole("rowgroup")[1]; + expect(within(tableBody).getAllByRole("row")).toHaveLength(items.length); + within(tableBody) + .getAllByRole("row") + .forEach((row, i) => + expect(row).toHaveTextContent(new RegExp(items[i].name, "i")) + ); +}); + +it("displays correct local time", () => { + const date = new Date("2000-01-01T12:00:00Z"); + vi.setSystemTime(date); + + const item = site({ timezone: "CET" }); + render(<SitesTable data={[item]} />); + + expect(screen.getByRole("table", { name: /sites/i })).toBeInTheDocument(); + expect(screen.getByText(/13:00 \(local time\)/i)).toBeInTheDocument(); +}); diff --git a/frontend/src/components/SitesList/components/SitesTable.tsx b/frontend/src/components/SitesList/components/SitesTable.tsx new file mode 100644 index 0000000..b6137e6 --- /dev/null +++ b/frontend/src/components/SitesList/components/SitesTable.tsx @@ -0,0 +1,232 @@ +import { + useReactTable, + flexRender, + ColumnDef, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, +} from "@tanstack/react-table"; +import { Input } from "@canonical/react-components"; +import { useMemo, useState } from "react"; +import pick from "lodash/fp/pick"; +import { format } from "date-fns"; +import { utcToZonedTime } from "date-fns-tz"; +import { Site } from "../../../api/types"; + +const createAccessor = + <T, K extends keyof T>(keys: K[] | K) => + (row: T) => + pick(keys, row); + +const SitesTable = ({ data }: { data: Site[] }) => { + const [columnVisibility, setColumnVisibility] = useState({}); + + const columns = useMemo<ColumnDef<Site, Partial<Site>>[]>( + () => [ + { + id: "select", + header: ({ table }) => ( + <div> + <Input + type="checkbox" + {...{ + checked: table.getIsAllRowsSelected(), + indeterminate: table.getIsSomeRowsSelected(), + onChange: table.getToggleAllRowsSelectedHandler(), + }} + /> + </div> + ), + cell: ({ row }) => ( + <div> + <Input + type="checkbox" + {...{ + checked: row.getIsSelected(), + disabled: !row.getCanSelect(), + indeterminate: row.getIsSomeSelected(), + onChange: row.getToggleSelectedHandler(), + }} + /> + </div> + ), + }, + { + id: "name", + accessorFn: createAccessor(["name", "url"]), + header: () => ( + <> + <div>Name</div> + <div className="u-text--muted">URL</div> + </> + ), + cell: ({ getValue }) => ( + <> + <div>{getValue().name}</div> + <div className="u-text--muted">{getValue().url}</div> + </> + ), + }, + { + id: "connection", + accessorFn: createAccessor(["connection", "last_seen"]), + header: () => ( + <> + <div>connection</div> + <div className="u-text--muted">last seen</div> + </> + ), + cell: ({ getValue }) => ( + <> + <div>{getValue().connection}</div> + <div className="u-text--muted">{getValue().last_seen}</div> + </> + ), + }, + { + id: "address", + accessorFn: createAccessor("address"), + header: () => ( + <> + <div>country</div> + <div className="u-text--muted">street, city, ZIP</div> + </> + ), + cell: ({ getValue }) => { + const { address } = getValue(); + const { countrycode, city, zip, street } = address || {}; + return ( + <> + <div>{countrycode}</div> + <div className="u-text--muted"> + {street}, {city}, {zip} + </div> + </> + ); + }, + }, + { + id: "time", + accessorFn: createAccessor("timezone"), + header: () => ( + <> + <div>local time</div> + <div className="u-text--muted">timezone</div> + </> + ), + cell: ({ getValue }) => { + const { timezone } = getValue(); + return timezone ? ( + <> + <div> + {format(utcToZonedTime(new Date(), timezone), "HH:mm")} (local + time) + </div> + <div className="u-text--muted">{timezone}</div> + </> + ) : null; + }, + }, + { + id: "status", + accessorFn: createAccessor("stats"), + header: () => ( + <> + <div>machines</div> + <div className="u-text--muted">aggregated status</div> + </> + ), + cell: ({ getValue }) => { + const { stats } = getValue(); + const { + machines, + ready_machines, + occupied_machines, + error_machines, + } = stats || {}; + return ( + <> + <div>{machines}</div> + <div className="u-text--muted"> + Ready: {ready_machines}, Occupied: {occupied_machines}, Error:{" "} + {error_machines} + </div> + </> + ); + }, + }, + ], + [] + ); + + const [rowSelection, setRowSelection] = useState({}); + + const table = useReactTable<Site>({ + data: data || [], + columns, + state: { + rowSelection, + columnVisibility, + }, + onColumnVisibilityChange: setColumnVisibility, + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + enableColumnResizing: false, + columnResizeMode: "onChange", + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getPaginationRowModel: getPaginationRowModel(), + debugTable: true, + debugHeaders: true, + debugColumns: true, + }); + + return ( + <table className="u-table-layout--auto" aria-label="sites"> + <thead> + {table.getHeaderGroups().map((headerGroup) => ( + <tr key={headerGroup.id}> + {headerGroup.headers.map((header) => { + return ( + <th key={header.id} colSpan={header.colSpan}> + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + {header.column.getCanResize() && ( + <div + onMouseDown={header.getResizeHandler()} + onTouchStart={header.getResizeHandler()} + className={`resizer ${ + header.column.getIsResizing() ? "isResizing" : "" + }`} + ></div> + )} + </th> + ); + })} + </tr> + ))} + </thead> + <tbody> + {table.getRowModel().rows.map((row) => { + return ( + <tr key={row.id}> + {row.getVisibleCells().map((cell) => { + return ( + <td key={cell.id}> + {flexRender(cell.column.columnDef.cell, cell.getContext())} + </td> + ); + })} + </tr> + ); + })} + </tbody> + </table> + ); +}; + +export default SitesTable; diff --git a/frontend/src/components/SitesList/types.ts b/frontend/src/components/SitesList/types.ts index c8495ac..e69de29 100644 --- a/frontend/src/components/SitesList/types.ts +++ b/frontend/src/components/SitesList/types.ts @@ -1,26 +0,0 @@ -export type Site = { - name: string; - url: string; // <full URL including protocol>, - connection: "stable" | "stale" | "lost"; - last_seen: string; // <ISO 8601 date>, - address: { - countrycode: string; // <alpha2 country code>, - city: string; - zip: string; - street: string; - }; - timezone: string; // <three letter abbreviation>, - stats: { - machines: number; - occupied_machines: number; - ready_machines: number; - error_machines: number; - }; -}; - -export type Sites = { - items: Site[]; - total: number; - page: number; - size: number; -}; diff --git a/frontend/src/hooks/api.test.ts b/frontend/src/hooks/api.test.ts new file mode 100644 index 0000000..46390b6 --- /dev/null +++ b/frontend/src/hooks/api.test.ts @@ -0,0 +1,27 @@ +import { renderHook, waitFor } from "@testing-library/react"; +import urls from "../api/urls"; +import { sites } from "../mocks/factories"; +import { createMockGetServer } from "../mocks/server"; +import { Providers } from "../test-utils"; +import { useSitesQuery } from "./api"; + +const sitesData = sites(); +const mockServer = createMockGetServer(urls.sites, sitesData); + +beforeAll(() => { + mockServer.listen(); +}); +afterEach(() => { + mockServer.resetHandlers(); +}); +afterAll(() => { + mockServer.close(); +}); + +it("should return sites", async () => { + const { result } = renderHook(() => useSitesQuery(), { wrapper: Providers }); + + await waitFor(() => expect(result.current.isFetchedAfterMount).toBe(true)); + + expect(result.current.data!.items).toEqual(sitesData.items); +}); diff --git a/frontend/src/hooks/api.ts b/frontend/src/hooks/api.ts new file mode 100644 index 0000000..e70f848 --- /dev/null +++ b/frontend/src/hooks/api.ts @@ -0,0 +1,5 @@ +import { useQuery } from "react-query"; +import { getSites } from "../api/handlers"; +import { Sites } from "../api/types"; + +export const useSitesQuery = () => useQuery<Sites>("/api/sites", getSites); diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index a8ae664..5d83917 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -3,7 +3,8 @@ import ReactDOM from "react-dom/client"; import App from "./App"; if (process.env.NODE_ENV === "development") { - import("./mocks/browser"); + const { worker } = await import("./mocks/browser"); + worker.start(); } ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( diff --git a/frontend/src/mocks/browser.ts b/frontend/src/mocks/browser.ts index 0f20911..34d0b39 100644 --- a/frontend/src/mocks/browser.ts +++ b/frontend/src/mocks/browser.ts @@ -1,11 +1,9 @@ -// src/mocks/browser.js +import urls from "../api/urls"; import { setupWorker, rest } from "msw"; import { sites } from "./factories"; -const worker = setupWorker( - rest.get("/api/sites", (_req, res, ctx) => { - return res(ctx.json(sites)); +export const worker = setupWorker( + rest.get(urls.sites, (_req, res, ctx) => { + return res(ctx.json(sites())); }) ); - -worker.start(); diff --git a/frontend/src/mocks/factories.ts b/frontend/src/mocks/factories.ts index 2e647e2..67bbc0b 100644 --- a/frontend/src/mocks/factories.ts +++ b/frontend/src/mocks/factories.ts @@ -1,4 +1,6 @@ -export const site = (site = {}) => ({ +import { Site } from "../api/types"; + +export const site = (site: Partial<Site> = {}): Site => ({ name: "maas-example-region", url: "http://maas.example.com", connection: "stable", @@ -19,7 +21,7 @@ export const site = (site = {}) => ({ ...site, }); -export const sites = { +export const sites = (sites = {}) => ({ items: [ site(), site({ @@ -37,4 +39,5 @@ export const sites = { total: 42, page: 1, size: 20, -}; + ...sites, +}); diff --git a/frontend/src/mocks/server.ts b/frontend/src/mocks/server.ts new file mode 100644 index 0000000..83178d5 --- /dev/null +++ b/frontend/src/mocks/server.ts @@ -0,0 +1,16 @@ +// src/mocks/browser.js +import { setupServer } from "msw/node"; +import { rest } from "msw"; +import { sites } from "./factories"; +import urls from "../api/urls"; + +const createMockGetServer = (endpoint: string, response: object) => + setupServer( + rest.get(endpoint, (_req, res, ctx) => { + return res(ctx.json(response)); + }) + ); + +const mockSitesServer = createMockGetServer(urls.sites, sites()); + +export { createMockGetServer, mockSitesServer }; diff --git a/frontend/src/test-utils.tsx b/frontend/src/test-utils.tsx index 543ac8a..622c7ae 100644 --- a/frontend/src/test-utils.tsx +++ b/frontend/src/test-utils.tsx @@ -25,3 +25,4 @@ const customRender = ( export * from "@testing-library/react"; export { customRender as render }; +export { Providers }; diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index f3fdf22..cde0616 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -2,10 +2,10 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react-swc"; import dotenv from "dotenv"; -dotenv.config(); +dotenv.config({ path: "../.env" }); // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], - server: { port: Number(process.env.UI_PORT) }, + server: { port: Number(process.env.VITE_UI_PORT) }, }); diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts index 8767b5f..2cd5a2c 100644 --- a/frontend/vitest.config.ts +++ b/frontend/vitest.config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from "vitest/config"; +import { defineConfig, configDefaults } from "vitest/config"; import react from "@vitejs/plugin-react-swc"; export default defineConfig({ @@ -6,6 +6,7 @@ export default defineConfig({ test: { globals: true, environment: "jsdom", - setupFiles: "./setupTests.js", + setupFiles: ["./setupTests.ts"], + exclude: [...configDefaults.exclude, "**/tests/**"], }, }); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 959ae32..18f7148 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -45,6 +45,22 @@ resolved "https://registry.yarnpkg.com/@canonical/latest-news/-/latest-news-1.4.1.tgz#dcdd445ac2268a54cf60f2f8c725b6bdeb285d71" integrity sha512-lwrikCj0Y11X8Ln8D+elp6Ri3mYjMjsAOJtadNEFzhBrgmg8TVGYJjOdwy8TvRaxy7fHnvVajIKAYWX44Tfj6w== +"@canonical/[email protected]": + version "0.38.0" + resolved "https://registry.yarnpkg.com/@canonical/react-components/-/react-components-0.38.0.tgz#180eb0412d62e29002a724386d08d5bea959b58b" + integrity sha512-0t20yrHamxCPCxlJu7ZjXMFexrfZXUSTZvVL+dSoTF8ZHTTi/NUnvgLMak8NWzVht9nOkVb6ltAA8pPSmMr8rg== + dependencies: + "@types/jest" "27.5.2" + "@types/node" "16.11.47" + "@types/react" "17.0.48" + "@types/react-dom" "17.0.17" + "@types/react-table" "7.7.12" + classnames "2.3.1" + nanoid "3.3.4" + prop-types "15.8.1" + react-table "7.8.0" + react-useportal "1.0.17" + "@esbuild/[email protected]": version "0.16.17" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz#cf91e86df127aa3d141744edafcba0abdc577d23" @@ -229,7 +245,7 @@ resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== -"@playwright/test@^1.31.1": +"@playwright/[email protected]": version "1.31.1" resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.31.1.tgz#39d6873dc46af135f12451d79707db7d1357455d" integrity sha512-IsytVZ+0QLDh1Hj83XatGp/GsI1CDJWbyDaBGbainsh0p2zC7F4toUocqowmjS6sQff2NGT3D9WbDj/3K2CJiA== @@ -315,6 +331,18 @@ "@swc/core-win32-ia32-msvc" "1.3.35" "@swc/core-win32-x64-msvc" "1.3.35" +"@tanstack/[email protected]": + version "8.7.9" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.7.9.tgz#9efcd168fb0080a7e0bc213b5eac8b55513babf4" + integrity sha512-6MbbQn5AupSOkek1+6IYu+1yZNthAKTRZw9tW92Vi6++iRrD1GbI3lKTjJalf8lEEKOqapPzQPE20nywu0PjCA== + dependencies: + "@tanstack/table-core" "8.7.9" + +"@tanstack/[email protected]": + version "8.7.9" + resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.7.9.tgz#0e975f8a5079972f1827a569079943d43257c42f" + integrity sha512-4RkayPMV1oS2SKDXfQbFoct1w5k+pvGpmX18tCXMofK/VDRdA2hhxfsQlMvsJ4oTX8b0CI4Y3GDKn5T425jBCw== + "@testing-library/dom@^8.5.0": version "8.20.0" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.0.tgz#914aa862cef0f5e89b98cc48e3445c4c921010f6" @@ -358,6 +386,13 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== +"@types/[email protected]": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@types/axios/-/axios-0.14.0.tgz#ec2300fbe7d7dddd7eb9d3abf87999964cafce46" + integrity sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ== + dependencies: + axios "*" + "@types/chai-subset@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@types/chai-subset/-/chai-subset-1.3.3.tgz#97893814e92abd2c534de422cb377e0e0bdaac94" @@ -409,11 +444,24 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/[email protected]": + version "27.5.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c" + integrity sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA== + dependencies: + jest-matcher-utils "^27.0.0" + pretty-format "^27.0.0" + "@types/js-levenshtein@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz#ba05426a43f9e4e30b631941e0aa17bf0c890ed5" integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g== +"@types/[email protected]": + version "4.14.191" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" + integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== + "@types/ms@*": version "0.7.31" resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" @@ -424,11 +472,23 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850" integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== +"@types/[email protected]": + version "16.11.47" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.47.tgz#efa9e3e0f72e7aa6a138055dace7437a83d9f91c" + integrity sha512-fpP+jk2zJ4VW66+wAMFoBJlx1bxmBKx4DUFf68UHgdGCOuyUTDlLWqsaNPJh7xhNDykyJ9eIzAygilP/4WoN8g== + "@types/prop-types@*": version "15.7.5" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/[email protected]": + version "17.0.17" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1" + integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg== + dependencies: + "@types/react" "^17" + "@types/[email protected]": version "18.0.10" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.10.tgz#3b66dec56aa0f16a6cc26da9e9ca96c35c0b4352" @@ -443,6 +503,13 @@ dependencies: "@types/react" "*" +"@types/[email protected]": + version "7.7.12" + resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.12.tgz#628011d3cb695b07c678704a61f2f1d5b8e567fd" + integrity sha512-bRUent+NR/WwtDGwI/BqhZ8XnHghwHw0HUKeohzB5xN3K2qKWYE5w19e7GCuOkL1CXD9Gi1HFy7TIm2AvgWUHg== + dependencies: + "@types/react" "*" + "@types/react@*": version "18.0.28" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065" @@ -452,6 +519,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/[email protected]": + version "17.0.48" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.48.tgz#a4532a8b91d7b27b8768b6fc0c3bccb760d15a6c" + integrity sha512-zJ6IYlJ8cYYxiJfUaZOQee4lh99mFihBoqkOSEGV+dFi9leROW6+PgstzQ+w3gWTnUfskALtQPGHK6dYmPj+2A== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/[email protected]": version "18.0.27" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.27.tgz#d9425abe187a00f8a5ec182b010d4fd9da703b71" @@ -461,6 +537,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^17": + version "17.0.53" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" + integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/scheduler@*": version "0.16.2" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" @@ -626,6 +711,11 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + [email protected]: version "10.4.13" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" @@ -643,6 +733,15 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +axios@*, [email protected]: + version "1.3.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" + integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -818,6 +917,11 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== [email protected]: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -881,6 +985,13 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + [email protected]: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -901,6 +1012,16 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== [email protected]: + version "2.0.0" + resolved "https://registry.yarnpkg.com/date-fns-tz/-/date-fns-tz-2.0.0.tgz#1b14c386cb8bc16fc56fe333d4fc34ae1d1099d5" + integrity sha512-OAtcLdB9vxSXTWHdT8b398ARImVwQMyjfYGkKD2zaGpHseG2UPHbHjXELReErZFxWdSLph3c2zOaaTyHfOhERQ== + [email protected]: + version "2.29.3" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" + integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== + debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -953,6 +1074,11 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dependency-graph@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" @@ -963,6 +1089,11 @@ detect-node@^2.0.4, detect-node@^2.1.0: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" @@ -985,7 +1116,7 @@ dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== -dotenv@^16.0.3: [email protected]: version "16.0.3" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== @@ -1125,6 +1256,11 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -1132,6 +1268,15 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" @@ -1536,6 +1681,16 @@ isarray@^2.0.5: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-diff@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.3.tgz#42f4eb34d0bf8c0fb08b0501069b87e8e84df347" @@ -1546,11 +1701,26 @@ jest-diff@^29.4.3: jest-get-type "^29.4.3" pretty-format "^29.4.3" +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + jest-get-type@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-matcher-utils@^27.0.0: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + jest-matcher-utils@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.3.tgz#ea68ebc0568aebea4c4213b99f169ff786df96a0" @@ -1627,7 +1797,7 @@ local-pkg@^0.4.2: resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963" integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g== -lodash@^4.17.15, lodash@^4.17.21: [email protected], lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -1640,7 +1810,7 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -loose-envify@^1.1.0: +loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -1685,6 +1855,18 @@ [email protected]: resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== [email protected]: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -1712,6 +1894,11 @@ mlly@^1.0.0, mlly@^1.1.0: pkg-types "^1.0.1" ufo "^1.0.1" [email protected]: + version "3.0.5" + resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb" + integrity sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ== + [email protected]: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -1754,7 +1941,7 @@ [email protected]: dependencies: big-integer "^1.6.16" -nanoid@^3.3.4: [email protected], nanoid@^3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== @@ -1781,6 +1968,11 @@ normalize-range@^0.1.2: resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" @@ -1972,7 +2164,7 @@ [email protected], postcss@^8.4.21: picocolors "^1.0.0" source-map-js "^1.0.2" -pretty-format@^27.0.2, pretty-format@^27.5.1: +pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== @@ -1995,6 +2187,20 @@ pretty-hrtime@^1.0.3: resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== [email protected]: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -2008,6 +2214,11 @@ [email protected]: loose-envify "^1.1.0" scheduler "^0.23.0" +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -2042,6 +2253,18 @@ [email protected]: dependencies: "@remix-run/router" "1.3.2" [email protected]: + version "7.8.0" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" + integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== + [email protected]: + version "1.0.17" + resolved "https://registry.yarnpkg.com/react-useportal/-/react-useportal-1.0.17.tgz#dcea1de8aa6d1ebd4bb3bb08075180a0e620f718" + integrity sha512-IS1NC+qQtp8e9Z9f8EPpW5whU85VuaT9Vxzw20vAAfKoRB9dlsnFmZ8NeNkbjNaue7Iy64Qsafs73M7qvkMzoA== + dependencies: + use-ssr "^1.0.22" + [email protected]: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -2372,6 +2595,11 @@ through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== [email protected]: + version "1.3.6" + resolved "https://registry.yarnpkg.com/timezone-mock/-/timezone-mock-1.3.6.tgz#44e4c5aeb57e6c07ae630a05c528fc4d9aab86f4" + integrity sha512-YcloWmZfLD9Li5m2VcobkCDNVaLMx8ohAb/97l/wYS3m+0TIEK5PFNMZZfRcusc6sFjIfxu8qcJT0CNnOdpqmg== + tinybench@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.3.1.tgz#14f64e6b77d7ef0b1f6ab850c7a808c6760b414d" @@ -2457,6 +2685,11 @@ update-browserslist-db@^1.0.10: escalade "^3.1.1" picocolors "^1.0.0" +use-ssr@^1.0.22: + version "1.0.24" + resolved "https://registry.yarnpkg.com/use-ssr/-/use-ssr-1.0.24.tgz#213a3df58f5ab9268e6fe1a57ad0a9de91e514d1" + integrity sha512-0MFps7ezL57/3o0yl4CvrHLlp9z20n1rQZV/lSRz7if+TUoM6POU1XdOvEjIgjgKeIhTEye1U0khrIYWCTWw4g== + util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
-- Mailing list: https://launchpad.net/~sts-sponsors Post to : [email protected] Unsubscribe : https://launchpad.net/~sts-sponsors More help : https://help.launchpad.net/ListHelp

