diff --git a/apps/parsley/cypress/integration/ansiLogs/ansi_filtering.ts b/apps/parsley/cypress/integration/ansiLogs/ansi_filtering.ts index fe09d2b0f..d092ef07e 100644 --- a/apps/parsley/cypress/integration/ansiLogs/ansi_filtering.ts +++ b/apps/parsley/cypress/integration/ansiLogs/ansi_filtering.ts @@ -5,6 +5,7 @@ describe("Filtering", () => { describe("Applying filters", () => { describe("Basic filtering", () => { beforeEach(() => { + cy.resetDrawerState(); cy.visit(logLink); cy.dataCy("paginated-virtual-list").should("be.visible"); }); @@ -20,6 +21,13 @@ describe("Filtering", () => { .and("match", /log-row-(0|5|6|297)/); }); }); + + it("does not corrupt filters that are large numbers", () => { + cy.addFilter("5553072873648668703"); + cy.dataCy("log-row-0").should("be.visible").dblclick({ force: true }); + cy.location("search").should("contain", "5553072873648668703"); + cy.dataCy("filter-5553072873648668703").should("be.visible"); + }); }); describe("Advanced filtering", () => { @@ -206,7 +214,8 @@ describe("Filtering", () => { cy.dataCy(`filter-${filter}`).within(() => { cy.get(`[aria-label="Edit filter"]`).click(); }); - cy.dataCy("edit-filter-name").clear().type("running"); + cy.dataCy("edit-filter-name").clear(); + cy.dataCy("edit-filter-name").type("running"); cy.contains("button", "Apply").click(); cy.location("search").should("contain", "filters=100running"); cy.get("[data-cy^='log-row-']") diff --git a/apps/parsley/cypress/integration/resmokeLogs/resmoke_filtering.ts b/apps/parsley/cypress/integration/resmokeLogs/resmoke_filtering.ts index eac770c04..cf9d4d850 100644 --- a/apps/parsley/cypress/integration/resmokeLogs/resmoke_filtering.ts +++ b/apps/parsley/cypress/integration/resmokeLogs/resmoke_filtering.ts @@ -5,6 +5,7 @@ describe("Filtering", () => { describe("Applying filters", () => { describe("Basic filtering", () => { beforeEach(() => { + cy.resetDrawerState(); cy.visit(logLink); cy.dataCy("paginated-virtual-list").should("be.visible"); }); @@ -20,6 +21,13 @@ describe("Filtering", () => { .and("match", /log-row-(0|5|6|130)/); }); }); + + it("does not corrupt filters that are large numbers", () => { + cy.addFilter("5553072873648668703"); + cy.dataCy("log-row-0").should("be.visible").dblclick({ force: true }); + cy.location("search").should("contain", "5553072873648668703"); + cy.dataCy("filter-5553072873648668703").should("be.visible"); + }); }); describe("Advanced filtering", () => { @@ -206,7 +214,8 @@ describe("Filtering", () => { cy.dataCy(`filter-${filter}`).within(() => { cy.get(`[aria-label="Edit filter"]`).click(); }); - cy.dataCy("edit-filter-name").clear().type("session"); + cy.dataCy("edit-filter-name").clear(); + cy.dataCy("edit-filter-name").type("session"); cy.contains("button", "Apply").click(); cy.location("search").should("contain", "filters=100session"); cy.get("[data-cy^='log-row-']") diff --git a/apps/parsley/package.json b/apps/parsley/package.json index 70149bf74..9da2a3c26 100644 --- a/apps/parsley/package.json +++ b/apps/parsley/package.json @@ -79,7 +79,7 @@ "linkifyjs": "4.1.3", "lossless-json": "4.0.1", "pluralize": "8.0.0", - "query-string": "9.0.0", + "query-string": "9.1.1", "react": "18.3.1", "react-dom": "18.3.1", "react-dropzone": "14.2.3", diff --git a/apps/parsley/src/components/BookmarksBar/index.tsx b/apps/parsley/src/components/BookmarksBar/index.tsx index 5cc38a480..c0e1ace6a 100644 --- a/apps/parsley/src/components/BookmarksBar/index.tsx +++ b/apps/parsley/src/components/BookmarksBar/index.tsx @@ -49,7 +49,11 @@ const BookmarksBar: React.FC = ({ }, []); // eslint-disable-line react-hooks/exhaustive-deps const lineNumbers = Array.from( - new Set([...bookmarks, shareLine ?? 0, failingLine ?? 0]), + new Set([ + ...bookmarks, + ...(shareLine ? [shareLine] : []), + ...(failingLine ? [failingLine] : []), + ]), ).sort((a, b) => a - b); return ( diff --git a/apps/parsley/src/components/Search/index.tsx b/apps/parsley/src/components/Search/index.tsx index d8e3bca50..edad933ad 100644 --- a/apps/parsley/src/components/Search/index.tsx +++ b/apps/parsley/src/components/Search/index.tsx @@ -28,9 +28,7 @@ const Search: React.FC = () => { const containerRef = useRef(null); const [filters, setFilters] = useFilterParam(); const [highlights, setHighlights] = useHighlightParam(); - const [searchParams, setSearchParams] = useQueryParams({ - parseNumbers: false, - }); + const [searchParams, setSearchParams] = useQueryParams(); const { hasLogs, logMetadata, diff --git a/apps/parsley/src/constants/queryParams.ts b/apps/parsley/src/constants/queryParams.ts index ed6ab6b02..a723f77e9 100644 --- a/apps/parsley/src/constants/queryParams.ts +++ b/apps/parsley/src/constants/queryParams.ts @@ -1,5 +1,6 @@ +import { ParseOptions } from "query-string"; + enum QueryParams { - Search = "search", Highlights = "highlights", Bookmarks = "bookmarks", Filters = "filters", @@ -12,4 +13,21 @@ enum QueryParams { SelectedLineRange = "selectedLineRange", } +const urlParseOptions: ParseOptions = { + arrayFormat: "comma", + parseBooleans: true, + parseNumbers: false, + types: { + bookmarks: "number", + filterLogic: "string", + filters: "string[]", + highlights: "string[]", + lower: "number", + selectedLineRange: "string", + shareLine: "number", + upper: "number", + }, +}; + export { QueryParams }; +export { urlParseOptions }; diff --git a/apps/parsley/src/hooks/useFilterParam/index.ts b/apps/parsley/src/hooks/useFilterParam/index.ts index e1d8b2b3f..d5ae44cb9 100644 --- a/apps/parsley/src/hooks/useFilterParam/index.ts +++ b/apps/parsley/src/hooks/useFilterParam/index.ts @@ -11,9 +11,7 @@ import { parseFilters, stringifyFilters } from "utils/query-string"; * @returns a tuple containing the parsed filters and a function to set the filters */ const useFilterParam = () => { - const [searchParams, setSearchParams] = useQueryParams({ - parseNumbers: false, - }); + const [searchParams, setSearchParams] = useQueryParams(); const parsedFilters = parseFilters( conditionalToArray(searchParams.filters ?? [], true) as string[], diff --git a/apps/parsley/src/hooks/useHighlightParam/index.ts b/apps/parsley/src/hooks/useHighlightParam/index.ts index 061694b06..69634dbb7 100644 --- a/apps/parsley/src/hooks/useHighlightParam/index.ts +++ b/apps/parsley/src/hooks/useHighlightParam/index.ts @@ -9,9 +9,7 @@ import { useQueryParams } from "hooks/useQueryParam"; * @returns a tuple containing the parsed highlights and a function to set the highlights */ const useHighlightParam = () => { - const [searchParams, setSearchParams] = useQueryParams({ - parseNumbers: false, - }); + const [searchParams, setSearchParams] = useQueryParams(); const parsedHighlights = useMemo( () => diff --git a/apps/parsley/src/hooks/useQueryParam/index.ts b/apps/parsley/src/hooks/useQueryParam/index.ts index 535e11895..087f34543 100644 --- a/apps/parsley/src/hooks/useQueryParam/index.ts +++ b/apps/parsley/src/hooks/useQueryParam/index.ts @@ -6,14 +6,14 @@ import { parseQueryString, stringifyQuery, } from "@evg-ui/lib/utils/query-string"; -import { QueryParams } from "constants/queryParams"; +import { QueryParams, urlParseOptions } from "constants/queryParams"; /** * `useQueryParams` returns all of the query params that exist in the url. * @param parseOptions - options which define how to parse params from the url (optional) * @returns a tuple containing the parsed query params and a function to set the query params */ -const useQueryParams = (parseOptions?: ParseOptions) => { +const useQueryParams = (parseOptions: ParseOptions = urlParseOptions) => { const [searchParams] = useSearchParams(); const navigate = useNavigate(); const setQueryString = useCallback( @@ -39,13 +39,15 @@ const useQueryParams = (parseOptions?: ParseOptions) => { * `useQueryParam` will default to the second argument if the query param is not present in the url. * @param param - the name of the query param * @param defaultParam - the default value of the query param + * @param parseOptions - options which define how to parse params from the url (optional) * @returns a tuple containing the parsed query param and a function to set the query param */ const useQueryParam = ( param: QueryParams, defaultParam: T, + parseOptions: ParseOptions = urlParseOptions, ): readonly [T, (set: T) => void] => { - const [searchParams, setSearchParams] = useQueryParams(); + const [searchParams, setSearchParams] = useQueryParams(parseOptions); const setQueryParam = useCallback( (value: T) => { @@ -58,7 +60,7 @@ const useQueryParam = ( } if (Array.isArray(paramValue)) { newParams[paramKey] = paramValue.map((v) => - v != null ? encodeURIComponent(v) : null, + v !== null ? encodeURIComponent(v) : null, ); } }); diff --git a/apps/parsley/src/hooks/useQueryParam/useQueryParam.test.tsx b/apps/parsley/src/hooks/useQueryParam/useQueryParam.test.tsx index 70cec9233..1c456a254 100644 --- a/apps/parsley/src/hooks/useQueryParam/useQueryParam.test.tsx +++ b/apps/parsley/src/hooks/useQueryParam/useQueryParam.test.tsx @@ -1,3 +1,4 @@ +import { ParseOptions } from "query-string"; import { MemoryRouter, useLocation } from "react-router-dom"; import { act, renderHook } from "@evg-ui/lib/test_utils"; import { QueryParams } from "constants/queryParams"; @@ -29,8 +30,18 @@ describe("useQueryParams", () => { }); const useQueryJointHook = (param: string, def: any) => { - const [queryParam, setQueryParam] = useQueryParam(param as QueryParams, def); - const [allQueryParams] = useQueryParams(); + const defaultParseOptions: ParseOptions = { + arrayFormat: "comma", + parseBooleans: true, + parseNumbers: true, + }; + + const [queryParam, setQueryParam] = useQueryParam( + param as QueryParams, + def, + defaultParseOptions, + ); + const [allQueryParams] = useQueryParams(defaultParseOptions); return { allQueryParams, queryParam, setQueryParam }; }; diff --git a/apps/spruce/package.json b/apps/spruce/package.json index b6891aa72..c37956177 100644 --- a/apps/spruce/package.json +++ b/apps/spruce/package.json @@ -122,7 +122,7 @@ "lodash.set": "4.3.2", "lodash.throttle": "4.1.1", "pluralize": "8.0.0", - "query-string": "9.0.0", + "query-string": "9.1.1", "react": "18.3.1", "react-dom": "18.3.1", "react-router-dom": "6.16.0", diff --git a/packages/lib/package.json b/packages/lib/package.json index f33fbc642..eed05c8b6 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -25,7 +25,7 @@ "@opentelemetry/auto-instrumentations-web": "^0.40.0", "@opentelemetry/instrumentation-user-interaction": "^0.41.0", "graphql": "16.8.1", - "query-string": "9.0.0" + "query-string": "9.1.1" }, "devDependencies": { "@evg-ui/eslint-config": "*", diff --git a/yarn.lock b/yarn.lock index 6670ccc8e..5dbaf24d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9934,10 +9934,10 @@ qs@~6.10.3: dependencies: side-channel "^1.0.4" -query-string@9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.0.0.tgz#1fe177cd95545600f0deab93f5fb02fd4e3e7273" - integrity sha512-4EWwcRGsO2H+yzq6ddHcVqkCQ2EFUSfDMEjF8ryp8ReymyZhIuaFRGLomeOQLkrzacMHoyky2HW0Qe30UbzkKw== +query-string@9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.1.1.tgz#dbfebb4196aeb2919915f2b2b81b91b965cf03a0" + integrity sha512-MWkCOVIcJP9QSKU52Ngow6bsAWAPlPK2MludXvcrS2bGZSl+T1qX9MZvRIkqUIkGLJquMJHWfsT6eRqUpp4aWg== dependencies: decode-uri-component "^0.4.1" filter-obj "^5.1.0"