Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: restore functionality for dropdowns, switchers and other API_ATOM derivatives #1594

Merged
merged 1 commit into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/ui/app/src/api-reference/ApiEndpointPage.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -5,13 +7,18 @@

export declare namespace ApiEndpointPage {
export interface Props {
item: ResolvedApiEndpoint;

Check warning on line 10 in packages/ui/app/src/api-reference/ApiEndpointPage.tsx

View workflow job for this annotation

GitHub Actions / lint

'ResolvedApiEndpoint' is deprecated.
showErrors: boolean;
types: Record<string, ResolvedTypeDefinition>;

Check warning on line 12 in packages/ui/app/src/api-reference/ApiEndpointPage.tsx

View workflow job for this annotation

GitHub Actions / lint

'ResolvedTypeDefinition' is deprecated.
}
}

export const ApiEndpointPage: React.FC<ApiEndpointPage.Props> = ({ item, showErrors, types }) => {
const setEnvironmentIds = useSetAtom(ALL_ENVIRONMENTS_ATOM);
if (item.type === "endpoint" || item.type === "websocket") {
setEnvironmentIds(item.environments.map((env) => env.id));
}

return (
<ApiPageContext.Provider value={true}>
<SingleApiPageContent item={item} showErrors={showErrors} types={types} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const EndpointUrl = React.forwardRef<HTMLDivElement, PropsWithChildren<En
})}
>
{showEnvironment && (
<span className="inline-flex whitespace-nowrap max-sm:hidden">
<span className="whitespace-nowrap max-sm:hidden">
<MaybeEnvironmentDropdown
selectedEnvironment={selectedEnvironment}
urlTextStyle="t-muted"
Expand Down
25 changes: 7 additions & 18 deletions packages/ui/app/src/atoms/environment.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,13 @@
import type { APIV1Read } from "@fern-api/fdr-sdk";
import { atom, useAtomValue } from "jotai";
import { atom, useAtomValue, useSetAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import { isEndpoint, isWebSocket } from "../resolver/types";
import { DEPRECATED_FLATTENED_APIS_ATOM } from "./apis";

// Capture all possible environments in a list, in useEffect at top level
export const ALL_ENVIRONMENTS_ATOM = atom((get) => {
const flatApis = get(DEPRECATED_FLATTENED_APIS_ATOM);
const allEnvironmentIds = new Set<string>();
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<string[]>([]);

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<string | undefined>("selected-environment", undefined);
Expand Down
50 changes: 42 additions & 8 deletions packages/ui/app/src/components/MaybeEnvironmentDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | undefined>(undefined);
const [initialState, setInitialState] = useState<string | undefined>(undefined);

const environmentIds = environmentFilters
? environmentFilters.filter((environmentFilter) => allEnvironmentIds.includes(environmentFilter))
Expand All @@ -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 ? (
<span key="url" className="whitespace-nowrap max-sm:hidden font-mono">
<span key="url" className="inline-flex whitespace-nowrap max-sm:hidden font-mono">
<FernInput
autoFocus={isEditingEnvironment.value}
size={inputValue?.length ?? 0}
Expand All @@ -68,7 +73,20 @@ export function MaybeEnvironmentDropdown({
onClick={(e) => {
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 === "" ||
Expand All @@ -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")}
Expand Down Expand Up @@ -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
}
/>
</FernDropdown>
) : (
Expand All @@ -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}`}
</span>
Expand Down
17 changes: 1 addition & 16 deletions packages/ui/app/src/playground/code-snippets/useSnippet.ts
Original file line number Diff line number Diff line change
@@ -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]);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Loading