diff --git a/packages/ui/app/src/api-reference/ApiEndpointPage.tsx b/packages/ui/app/src/api-reference/ApiEndpointPage.tsx index f03ede4713..3a358f4bd8 100644 --- a/packages/ui/app/src/api-reference/ApiEndpointPage.tsx +++ b/packages/ui/app/src/api-reference/ApiEndpointPage.tsx @@ -1,3 +1,5 @@ +import { useSetAtom } from "jotai"; +import { ALL_ENVIRONMENTS_ATOM } from "../atoms/environment"; import { ApiPageContext } from "../contexts/api-page"; import type { ResolvedApiEndpoint, ResolvedTypeDefinition } from "../resolver/types"; import { BuiltWithFern } from "../sidebar/BuiltWithFern"; @@ -12,6 +14,11 @@ export declare namespace ApiEndpointPage { } export const ApiEndpointPage: React.FC = ({ item, showErrors, types }) => { + const setEnvironmentIds = useSetAtom(ALL_ENVIRONMENTS_ATOM); + if (item.type === "endpoint" || item.type === "websocket") { + setEnvironmentIds(item.environments.map((env) => env.id)); + } + return ( diff --git a/packages/ui/app/src/api-reference/endpoints/EndpointUrl.tsx b/packages/ui/app/src/api-reference/endpoints/EndpointUrl.tsx index 26418aad22..c0718923d6 100644 --- a/packages/ui/app/src/api-reference/endpoints/EndpointUrl.tsx +++ b/packages/ui/app/src/api-reference/endpoints/EndpointUrl.tsx @@ -96,7 +96,7 @@ export const EndpointUrl = React.forwardRef {showEnvironment && ( - + { - const flatApis = get(DEPRECATED_FLATTENED_APIS_ATOM); - const allEnvironmentIds = new Set(); - Object.values(flatApis).forEach((api) => { - api.endpoints.forEach((endpoint) => { - if (isEndpoint(endpoint) || isWebSocket(endpoint)) { - endpoint.environments.forEach((environment: APIV1Read.Environment) => { - allEnvironmentIds.add(environment.id); - }); - } - }); - }); - return Array.from(allEnvironmentIds); -}); +export const ALL_ENVIRONMENTS_ATOM = atom([]); + +export const useSetAllEnvironments = (allEnvironmentIds: string[]): void => { + const setAllEnvironments = useSetAtom(ALL_ENVIRONMENTS_ATOM); + setAllEnvironments(allEnvironmentIds); +}; // Get or select an environment export const SELECTED_ENVIRONMENT_ATOM = atomWithStorage("selected-environment", undefined); diff --git a/packages/ui/app/src/components/MaybeEnvironmentDropdown.tsx b/packages/ui/app/src/components/MaybeEnvironmentDropdown.tsx index 4a27e2a3c5..2e47fb9d8f 100644 --- a/packages/ui/app/src/components/MaybeEnvironmentDropdown.tsx +++ b/packages/ui/app/src/components/MaybeEnvironmentDropdown.tsx @@ -31,6 +31,7 @@ export function MaybeEnvironmentDropdown({ const [selectedEnvironmentId, setSelectedEnvironmentId] = useAtom(SELECTED_ENVIRONMENT_ATOM); const [playgroundEnvironment, setPlaygroundEnvironment] = useAtom(PLAYGROUND_ENVIRONMENT_ATOM); const [inputValue, setInputValue] = useState(undefined); + const [initialState, setInitialState] = useState(undefined); const environmentIds = environmentFilters ? environmentFilters.filter((environmentFilter) => allEnvironmentIds.includes(environmentFilter)) @@ -54,12 +55,16 @@ export function MaybeEnvironmentDropdown({ inputValue != null && inputValue !== "" && parse(inputValue).host != null && parse(inputValue).protocol != null; const urlProtocol = url ? url.protocol : ""; - const fullyQualifiedDomainAndBasePath = url ? (url.pathname !== "/" ? `${url.host}${url.pathname}` : url.host) : ""; + const fullyQualifiedDomainAndBasePath = url + ? url.pathname != null && url.pathname !== "/" + ? `${url.host}${url.pathname}` + : url.host + : ""; return ( <> {isEditingEnvironment.value ? ( - + { e.stopPropagation(); }} - onBlur={isEditingEnvironment.setFalse} + onBlur={(e) => { + if (isValidInput) { + if (playgroundEnvironment) { + setInputValue(playgroundEnvironment); + } + isEditingEnvironment.setFalse(); + } else { + e.preventDefault(); + e.stopPropagation(); + setInputValue(initialState); + setPlaygroundEnvironment(initialState); + isEditingEnvironment.setFalse(); + } + }} onValueChange={(value) => { if ( value === "" || @@ -82,16 +100,18 @@ export function MaybeEnvironmentDropdown({ setPlaygroundEnvironment(value); } }} - onKeyDown={(e) => { + onKeyDownCapture={(e) => { if (e.key === "Enter" && isValidInput) { if (playgroundEnvironment) { setInputValue(playgroundEnvironment); } isEditingEnvironment.setFalse(); } else if (e.key === "Escape") { - setInputValue(playgroundEnvironment ?? selectedEnvironment?.baseUrl); - isEditingEnvironment.setFalse(); e.preventDefault(); + e.stopPropagation(); + setInputValue(initialState); + setPlaygroundEnvironment(initialState); + isEditingEnvironment.setFalse(); } }} className={cn("p-0", isValidInput ? "" : "error", "h-auto", "flex flex-col")} @@ -128,7 +148,14 @@ export function MaybeEnvironmentDropdown({ size={small ? "small" : "normal"} variant="outlined" mono={true} - onDoubleClick={editable ? isEditingEnvironment.setTrue : () => undefined} + onDoubleClick={ + editable + ? () => { + setInitialState(inputValue); + isEditingEnvironment.setTrue(); + } + : () => undefined + } /> ) : ( @@ -141,7 +168,14 @@ export function MaybeEnvironmentDropdown({ small ? "text-xs" : "text-sm", "hover:shadow-lg", )} - onDoubleClick={isEditingEnvironment.setTrue} + onDoubleClick={ + editable + ? () => { + setInitialState(inputValue); + isEditingEnvironment.setTrue(); + } + : () => undefined + } > {`${urlProtocol}//${fullyQualifiedDomainAndBasePath}`} diff --git a/packages/ui/app/src/playground/code-snippets/useSnippet.ts b/packages/ui/app/src/playground/code-snippets/useSnippet.ts index cb8b47dce0..bcb25a1478 100644 --- a/packages/ui/app/src/playground/code-snippets/useSnippet.ts +++ b/packages/ui/app/src/playground/code-snippets/useSnippet.ts @@ -1,25 +1,10 @@ -import { EndpointDefinition } from "@fern-api/fdr-sdk/api-definition"; import { useMemo } from "react"; -import { usePlaygroundBaseUrl } from "../utils/select-environment"; import { PlaygroundCodeSnippetResolver } from "./resolver"; import { useApiDefinition } from "./useApiDefinition"; -export const resolveEnvironmentUrlInCodeSnippet = ( - endpoint: EndpointDefinition, - replacementBaseUrl: string | undefined, - requestCodeSnippet: string, -): string => { - const urlToReplace = endpoint.environments?.find((env) => requestCodeSnippet.includes(env.baseUrl))?.baseUrl; - return urlToReplace && replacementBaseUrl - ? requestCodeSnippet.replace(urlToReplace, replacementBaseUrl) - : requestCodeSnippet; -}; - export function useSnippet(resolver: PlaygroundCodeSnippetResolver, lang: "curl" | "python" | "typescript"): string { const apiDefinition = useApiDefinition(resolver.context.node.apiDefinitionId, resolver.isSnippetTemplatesEnabled); - const baseUrl = usePlaygroundBaseUrl(resolver.context.endpoint); // Resolve the code snippet - const code = useMemo(() => resolver.resolve(lang, apiDefinition), [resolver, lang, apiDefinition]); - return resolveEnvironmentUrlInCodeSnippet(resolver.context.endpoint, baseUrl, code); + return useMemo(() => resolver.resolve(lang, apiDefinition), [resolver, lang, apiDefinition]); } diff --git a/packages/ui/app/src/playground/utils/select-environment.ts b/packages/ui/app/src/playground/utils/select-environment.ts index 1e73f7feef..e13237de97 100644 --- a/packages/ui/app/src/playground/utils/select-environment.ts +++ b/packages/ui/app/src/playground/utils/select-environment.ts @@ -22,5 +22,5 @@ export function useSelectedEnvironment(endpoint: WebSocketChannel | EndpointDefi export function usePlaygroundBaseUrl(endpoint: WebSocketChannel | EndpointDefinition): string | undefined { const environment = useSelectedEnvironment(endpoint); const playgroundBaseUrl = usePlaygroundEnvironment(); - return environment?.baseUrl ?? playgroundBaseUrl; + return playgroundBaseUrl ?? environment?.baseUrl; }