- Notifications
You must be signed in to change notification settings - Fork947
fix(site): tighten interface design for various frontend utility functions#18894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
base:main
Are you sure you want to change the base?
Changes fromall commits
bb05088
a119d70
5dc67cf
4c77bff
4833ae1
b8d92fa
1662a55
e326414
cc00106
7304644
b512e18
cb92b79
86d19b8
330d91d
fb9d8f2
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -48,21 +48,6 @@ export const templates = ( | ||
}; | ||
}; | ||
Comment on lines -51 to -64 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Weren't used at all | ||
export const templateACL = (templateId: string) => { | ||
return { | ||
queryKey: ["templateAcl", templateId], | ||
@@ -121,9 +106,11 @@ export const templateExamples = () => { | ||
}; | ||
}; | ||
export const templateVersionRoot: string = "templateVersion"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. This ends up being more important for the upcoming PR, but I really, really think that we need to find the time to formalize our entire data layer, and be way more deliberate with our React Query caching I fully expect our caching to create a super, super hard-to-recreate bug sometime in the future, because it's a bunch of semi-structured global mutable state right now MemberAuthor
| ||
export const templateVersion = (versionId: string) => { | ||
return { | ||
queryKey: [templateVersionRoot, versionId], | ||
queryFn: () => API.getTemplateVersion(versionId), | ||
}; | ||
}; | ||
@@ -134,7 +121,7 @@ export const templateVersionByName = ( | ||
versionName: string, | ||
) => { | ||
return { | ||
queryKey: [templateVersionRoot, organizationId, templateName, versionName], | ||
queryFn: () => | ||
API.getTemplateVersionByName(organizationId, templateName, versionName), | ||
}; | ||
@@ -153,7 +140,7 @@ export const templateVersions = (templateId: string) => { | ||
}; | ||
export const templateVersionVariablesKey = (versionId: string) => [ | ||
templateVersionRoot, | ||
versionId, | ||
"variables", | ||
]; | ||
@@ -216,7 +203,7 @@ export const templaceACLAvailable = ( | ||
}; | ||
const templateVersionExternalAuthKey = (versionId: string) => [ | ||
templateVersionRoot, | ||
versionId, | ||
"externalAuth", | ||
]; | ||
@@ -257,21 +244,21 @@ const createTemplateFn = async (options: CreateTemplateOptions) => { | ||
export const templateVersionLogs = (versionId: string) => { | ||
return { | ||
queryKey: [templateVersionRoot, versionId, "logs"], | ||
queryFn: () => API.getTemplateVersionLogs(versionId), | ||
}; | ||
}; | ||
export const richParameters = (versionId: string) => { | ||
return { | ||
queryKey: [templateVersionRoot, versionId, "richParameters"], | ||
queryFn: () => API.getTemplateVersionRichParameters(versionId), | ||
}; | ||
}; | ||
export const resources = (versionId: string) => { | ||
return { | ||
queryKey: [templateVersionRoot, versionId, "resources"], | ||
queryFn: () => API.getTemplateVersionResources(versionId), | ||
}; | ||
}; | ||
@@ -293,7 +280,7 @@ export const previousTemplateVersion = ( | ||
) => { | ||
return { | ||
queryKey: [ | ||
templateVersionRoot, | ||
organizationId, | ||
templateName, | ||
versionName, | ||
@@ -313,7 +300,7 @@ export const previousTemplateVersion = ( | ||
export const templateVersionPresets = (versionId: string) => { | ||
return { | ||
queryKey: [templateVersionRoot, versionId, "presets"], | ||
queryFn: () => API.getTemplateVersionPresets(versionId), | ||
}; | ||
}; | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -16,7 +16,6 @@ import { useDebouncedFunction } from "hooks/debounce"; | ||
import { ExternalLinkIcon } from "lucide-react"; | ||
import { ChevronDownIcon } from "lucide-react"; | ||
import { type FC, type ReactNode, useEffect, useRef, useState } from "react"; | ||
type PresetFilter = { | ||
name: string; | ||
@@ -27,35 +26,55 @@ type FilterValues = Record<string, string | undefined>; | ||
type UseFilterConfig = { | ||
/** | ||
* The fallback value to use in the event that no filter params can be | ||
* parsed from the search params object. | ||
*/ | ||
fallbackFilter?: string; | ||
searchParams: URLSearchParams; | ||
onSearchParamsChange: (newParams: URLSearchParams) => void; | ||
Comment on lines +33 to +34 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Decoupled type signature from React Router. Splitting this up gives us a lot more freedom, which comes up later in this PR | ||
onUpdate?: (newValue: string) => void; | ||
}; | ||
export type UseFilterResult = Readonly<{ | ||
query: string; | ||
values: FilterValues; | ||
used: boolean; | ||
update: (newValues: string | FilterValues) => void; | ||
debounceUpdate: (newValues: string | FilterValues) => void; | ||
cancelDebounce: () => void; | ||
}>; | ||
Comment on lines +38 to +45 MemberAuthor
| ||
export const useFilterParamsKey = "filter"; | ||
export const useFilter = ({ | ||
fallbackFilter = "", | ||
searchParams, | ||
onSearchParamsChange, | ||
onUpdate, | ||
}: UseFilterConfig): UseFilterResult => { | ||
const query = searchParams.get(useFilterParamsKey) ?? fallbackFilter; | ||
const update = (newValues: string | FilterValues) => { | ||
const serialized = | ||
typeof newValues === "string" ? newValues : stringifyFilter(newValues); | ||
const noUpdateNeeded = query === serialized; | ||
if (noUpdateNeeded) { | ||
return; | ||
} | ||
/** | ||
* @todo 2025-07-15 - We have a slightly nasty bug here, where trying to | ||
* update state the "React way" causes our code to break. | ||
* | ||
* In theory, it would be better to make a copy of the search params. We | ||
* can then mutate and dispatch the copy instead of the original. Doing | ||
* that causes other parts of our existing logic to break, though. | ||
* That's a sign that our other code is slightly broken, and only just | ||
* happens to work by chance right now. | ||
*/ | ||
searchParams.set(useFilterParamsKey, serialized); | ||
onSearchParamsChange(searchParams); | ||
onUpdate?.(serialized); | ||
}; | ||
const { debounced: debounceUpdate, cancelDebounce } = useDebouncedFunction( | ||
@@ -73,8 +92,6 @@ export const useFilter = ({ | ||
}; | ||
}; | ||
const parseFilterQuery = (filterQuery: string): FilterValues => { | ||
if (filterQuery === "") { | ||
return {}; | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,41 @@ | ||
import { DEFAULT_RECORDS_PER_PAGE } from "components/PaginationWidget/utils"; | ||
const paginationKey = "page"; | ||
type UsePaginationOptions = Readonly<{ | ||
searchParams: URLSearchParams; | ||
onSearchParamsChange: (newParams: URLSearchParams) => void; | ||
}>; | ||
type UsePaginationResult = Readonly<{ | ||
page: number; | ||
limit: number; | ||
offset: number; | ||
goToPage: (page: number) => void; | ||
}>; | ||
Comment on lines +10 to +15 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. We were missing a return type altogether before | ||
export function usePagination( | ||
options: UsePaginationOptions, | ||
): UsePaginationResult { | ||
const { searchParams, onSearchParamsChange } = options; | ||
const limit = DEFAULT_RECORDS_PER_PAGE; | ||
const rawPage = Number.parseInt(searchParams.get(paginationKey) || "1", 10); | ||
const page = Number.isNaN(rawPage) || rawPage <= 0 ? 1 : rawPage; | ||
return { | ||
page, | ||
limit, | ||
offset: Math.max(0, (page - 1) * limit), | ||
goToPage: (newPage) => { | ||
const abortNavigation = | ||
page === newPage || !Number.isFinite(page) || !Number.isInteger(page); | ||
if (abortNavigation) { | ||
return; | ||
} | ||
Comment on lines +30 to +34 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Another render optimization There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. What is this render optimization? Not re-rendering the page if it's the same page? MemberAuthor
| ||
const copy = new URLSearchParams(searchParams); | ||
copy.set("page", page.toString()); | ||
onSearchParamsChange(copy); | ||
}, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -108,9 +108,7 @@ export const CreateWorkspacePageViewExperimental: FC< | ||
owner, | ||
setOwner, | ||
}) => { | ||
const [suggestedName, setSuggestedName] = useState(generateWorkspaceName); | ||
MemberAuthor
| ||
const [showPresetParameters, setShowPresetParameters] = useState(false); | ||
const id = useId(); | ||
const workspaceNameInputRef = useRef<HTMLInputElement>(null); | ||
@@ -124,14 +122,14 @@ export const CreateWorkspacePageViewExperimental: FC< | ||
// Only touched fields are sent to the websocket | ||
// Autofilled parameters are marked as touched since they have been modified | ||
const initialTouched = parameters.reduce<Record<string, boolean>>( | ||
(touched, parameter) => { | ||
if (autofillByName[parameter.name] !== undefined) { | ||
touched[parameter.name] = true; | ||
} | ||
return touched; | ||
}, | ||
{}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Removed type assertion to remove risk of accidentally silencing the compiler | ||
); | ||
// The form parameters values hold the working state of the parameters that will be submitted when creating a workspace | ||
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.