From 4563619319170fe8f5d41edadc93a0a1c70b6784 Mon Sep 17 00:00:00 2001 From: luizakp Date: Wed, 10 Apr 2024 16:52:25 -0300 Subject: [PATCH 1/3] chore: check if url already has a query param --- src/components/DataTable/useSWRDataTable.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/DataTable/useSWRDataTable.tsx b/src/components/DataTable/useSWRDataTable.tsx index 0f47a40..1cd6679 100644 --- a/src/components/DataTable/useSWRDataTable.tsx +++ b/src/components/DataTable/useSWRDataTable.tsx @@ -16,7 +16,10 @@ function formatRequestParams(originalObj) { const dataTableFetcher = async ([url, paramsObject]) => { const formattedParams = formatRequestParams(paramsObject); const params = serializeQuery(formattedParams); - const response = await fetch(`${url}?${params}`, { + + const separator = url.includes("?") ? "&" : "?"; + + const response = await fetch(`${url}${separator}${params}`, { headers: { Accept: "application/json", }, From 57ab97d7bb69f1ccb3d6fb5a0cdf03c0149219fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 11 Apr 2024 11:24:39 -0300 Subject: [PATCH 2/3] chore(i18n): add translation for `Reset` in DataTableToolbar --- src/components/DataTable/DataTableToolbar.tsx | 4 ++-- src/locales/en/translation.json | 1 + src/locales/pt-BR/translation.json | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/DataTable/DataTableToolbar.tsx b/src/components/DataTable/DataTableToolbar.tsx index ce30a51..3c04c77 100644 --- a/src/components/DataTable/DataTableToolbar.tsx +++ b/src/components/DataTable/DataTableToolbar.tsx @@ -1,7 +1,7 @@ import { Cross2Icon } from "@radix-ui/react-icons"; import React, { useEffect, useRef } from "react"; -import { useTranslation } from "react-i18next"; +import { Trans, useTranslation } from "react-i18next"; import { Button, Input } from "#/components/ui"; import { DataTableFacetedFilter } from "./DataTableFacetedFilter"; @@ -77,7 +77,7 @@ export function DataTableToolbar({ onClick={() => table.resetColumnFilters()} className="h-8 px-2 lg:px-3" > - Reset + Reset )} diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index a2d4b5c..62be307 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -7,6 +7,7 @@ "Items per page": "Items per page", "Page {{currentPage}} of": "Page {{currentPage}} of", "Search": "Search...", + "Reset": "Reset", "View": "View", "Toggle columns": "Toggle columns", "Something went wrong!": "Something went wrong!", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 714e739..0bb8718 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -7,6 +7,7 @@ "Items per page": "Itens por página", "Page {{currentPage}} of": "Página {{currentPage}} de", "Search": "Buscar...", + "Reset": "Remover", "View": "Visualizar", "Toggle columns": "Alternar colunas", "Something went wrong!": "Algo deu errado!", From 1822396c5c4bfcedbf4527317acaf50f22b99440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Ribeiro?= Date: Thu, 11 Apr 2024 11:36:54 -0300 Subject: [PATCH 3/3] chore(refactor): add constructFullUrlWithParams and tests --- src/components/DataTable/useSWRDataTable.tsx | 7 ++- src/lib/constructFullUrlWithParams.ts | 21 ++++++++ tests/lib/constructFullUrlWithParams.test.ts | 53 ++++++++++++++++++++ 3 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/lib/constructFullUrlWithParams.ts create mode 100644 tests/lib/constructFullUrlWithParams.test.ts diff --git a/src/components/DataTable/useSWRDataTable.tsx b/src/components/DataTable/useSWRDataTable.tsx index 1cd6679..9b16267 100644 --- a/src/components/DataTable/useSWRDataTable.tsx +++ b/src/components/DataTable/useSWRDataTable.tsx @@ -1,6 +1,6 @@ import useSWR from "swr"; import { useTableState } from "./useTableState"; -import { serializeQuery } from "#/lib/serializeQuery"; +import { constructFullUrlWithParams } from "#/lib/constructFullUrlWithParams"; function formatRequestParams(originalObj) { return { @@ -15,11 +15,10 @@ function formatRequestParams(originalObj) { const dataTableFetcher = async ([url, paramsObject]) => { const formattedParams = formatRequestParams(paramsObject); - const params = serializeQuery(formattedParams); - const separator = url.includes("?") ? "&" : "?"; + const fullUrl = constructFullUrlWithParams(url, formattedParams); - const response = await fetch(`${url}${separator}${params}`, { + const response = await fetch(fullUrl, { headers: { Accept: "application/json", }, diff --git a/src/lib/constructFullUrlWithParams.ts b/src/lib/constructFullUrlWithParams.ts new file mode 100644 index 0000000..12aeb0f --- /dev/null +++ b/src/lib/constructFullUrlWithParams.ts @@ -0,0 +1,21 @@ +import { deserializeQuery, serializeQuery } from "./serializeQuery"; + +export function constructFullUrlWithParams( + baseUrl: string, + queryParams: Record +) { + if (typeof baseUrl !== "string") { + throw new Error("Base URL must be a string."); + } + + const url = new URL(baseUrl); + + const existingParams = deserializeQuery(url.search); + + const mergedParams = { ...existingParams, ...queryParams }; + const serializedParams = serializeQuery(mergedParams); + + url.search = serializedParams; + + return url.toString(); +} diff --git a/tests/lib/constructFullUrlWithParams.test.ts b/tests/lib/constructFullUrlWithParams.test.ts new file mode 100644 index 0000000..759fe42 --- /dev/null +++ b/tests/lib/constructFullUrlWithParams.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, it } from "vitest"; +import { constructFullUrlWithParams } from "../../src/lib/constructFullUrlWithParams"; + +describe("constructFullUrlWithParams", () => { + it("should throw an error if the base URL is not a string", () => { + // @ts-expect-error + expect(() => constructFullUrlWithParams(null, {})).toThrow( + "Base URL must be a string." + ); + }); + + it("should correctly add simple query parameters to a base URL", () => { + const baseUrl = "http://example.com"; + const queryParams = { name: "John", age: 30 }; + const result = constructFullUrlWithParams(baseUrl, queryParams); + expect(result).toBe("http://example.com/?name=John&age=30"); + }); + + it("should handle nested objects and arrays in query parameters", () => { + const baseUrl = "http://example.com"; + const queryParams = { + user: { name: "John", roles: ["admin", "user"] }, + active: true, + }; + const result = constructFullUrlWithParams(baseUrl, queryParams); + expect(result).toBe( + "http://example.com/?user[name]=John&user[roles][]=admin&user[roles][]=user&active=true" + ); + }); + + it("should handle special characters in query parameters", () => { + const baseUrl = "http://example.com"; + const queryParams = { greeting: "hello world", symbols: "!@#$%^&*" }; + const result = constructFullUrlWithParams(baseUrl, queryParams); + expect(result).toBe( + "http://example.com/?greeting=hello%20world&symbols=!%40%23%24%25%5E%26*" + ); + }); + + it("should merge and serialize existing query parameters with new ones", () => { + const baseUrl = "http://example.com?name=Jane&active=true"; + const queryParams = { age: 30, active: false }; + const result = constructFullUrlWithParams(baseUrl, queryParams); + expect(result).toBe("http://example.com/?name=Jane&active=false&age=30"); + }); + + it("should override existing parameters with new ones", () => { + const baseUrl = "http://example.com?name=Jane"; + const queryParams = { name: "John", age: 30 }; + const result = constructFullUrlWithParams(baseUrl, queryParams); + expect(result).toBe("http://example.com/?name=John&age=30"); + }); +});