Skip to content

Commit

Permalink
Explore Endpoints: Params tab view
Browse files Browse the repository at this point in the history
  • Loading branch information
quietbits committed Mar 6, 2024
1 parent f0cada8 commit b1d6fdf
Show file tree
Hide file tree
Showing 8 changed files with 494 additions and 44 deletions.
182 changes: 138 additions & 44 deletions src/app/(sidebar)/explore-endpoints/[[...pages]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,155 @@
"use client";

import { useState } from "react";
import { usePathname } from "next/navigation";
import { Card, Icon, Text } from "@stellar/design-system";
import {
Alert,
Button,
Card,
Checkbox,
CopyText,
Icon,
Input,
Text,
} from "@stellar/design-system";

import { InfoCards } from "@/components/InfoCards";
import { TabView } from "@/components/TabView";
import { SdsLink } from "@/components/SdsLink";
import { Routes } from "@/constants/routes";

const infoCards = [
{
id: "soroban-rpc",
title: "RPC Endpoints",
description: "TODO: add text",
buttonLabel: "See docs",
buttonIcon: <Icon.LinkExternal01 />,
buttonAction: () =>
window.open("https://soroban.stellar.org/api/methods", "_blank"),
},
{
id: "horizon",
title: "Horizon Endpoints",
description: "TODO: add text",
buttonLabel: "See docs",
buttonIcon: <Icon.LinkExternal01 />,
buttonAction: () =>
window.open(
"https://developers.stellar.org/api/horizon/resources/",
"_blank",
),
},
];
import { WithInfoText } from "@/components/WithInfoText";

export default function ExploreEndpoints() {
const pathname = usePathname();

const [activeTab, setActiveTab] = useState("endpoints-tab-params");

if (pathname === Routes.EXPLORE_ENDPOINTS) {
return <ExploreEndpointsLandingPage />;
}

const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
// TODO: handle submit
};

const renderEndpointUrl = () => {
return (
<>
<Card>
<div className="CardText">
<Text size="lg" as="h1" weight="medium">
Endpoints
</Text>

<Text size="sm" as="p">
TODO: add text
</Text>
</div>
</Card>
<InfoCards infoCards={infoCards} />
</>
<div className="Endpoints__urlBar">
<Input
id="endpoint-url"
fieldSize="md"
// TODO: update URL
value="https://"
readOnly
disabled
// TODO: set request type
leftElement={<div className="Endpoints__input__requestType">GET</div>}
/>
{/* TODO: disable if can't submit */}
<Button size="md" variant="secondary" type="submit">
Submit
</Button>
{/* TODO: add text to copy */}
<CopyText textToCopy="">
<Button size="md" variant="tertiary" icon={<Icon.Copy01 />}></Button>
</CopyText>
</div>
);
}
};

return renderPage(pathname);
const renderFields = () => {
return (
<div className="Endpoints__content">
<div className="Endpoints__content__inputs">
{/* TODO: render fields for path */}
{`Explore Endpoints: ${pathname}`}
</div>

<WithInfoText href="https://developers.stellar.org/network/horizon/structure/streaming">
<Checkbox
id="streaming-mode"
label="Server-Sent Events (streaming mode)"
fieldSize="md"
/>
</WithInfoText>
</div>
);
};

return (
<>
<form onSubmit={handleSubmit}>
<TabView
heading={{ title: "Heading title", href: "http://stellar.org" }}
staticTop={renderEndpointUrl()}
tab1={{
id: "endpoints-tab-params",
label: "Params",
content: renderFields(),
}}
tab2={{
id: "endpoints-tab-json",
label: "JSON Response",
content: <div>TODO: render JSON</div>,
}}
onTabChange={(id) => {
setActiveTab(id);
}}
activeTabId={activeTab}
/>
</form>
<Alert variant="primary" placement="inline">
This tool can be used to run queries against the{" "}
<SdsLink href="https://developers.stellar.org/network/horizon">
REST API endpoints
</SdsLink>{" "}
on the Horizon server. Horizon is the client facing library for the
Stellar ecosystem.
</Alert>
</>
);
}

const renderPage = (pathname: string) => {
// TODO: add switch to render path component
return <div>{`Explore Endpoints: ${pathname}`}</div>;
const ExploreEndpointsLandingPage = () => {
const infoCards = [
{
id: "soroban-rpc",
title: "RPC Endpoints",
description: "TODO: add text",
buttonLabel: "See docs",
buttonIcon: <Icon.LinkExternal01 />,
buttonAction: () =>
window.open("https://soroban.stellar.org/api/methods", "_blank"),
},
{
id: "horizon",
title: "Horizon Endpoints",
description: "TODO: add text",
buttonLabel: "See docs",
buttonIcon: <Icon.LinkExternal01 />,
buttonAction: () =>
window.open(
"https://developers.stellar.org/api/horizon/resources/",
"_blank",
),
},
];

return (
<>
<Card>
<div className="CardText">
<Text size="lg" as="h1" weight="medium">
Endpoints
</Text>

<Text size="sm" as="p">
TODO: add text
</Text>
</div>
</Card>
<InfoCards infoCards={infoCards} />
</>
);
};
117 changes: 117 additions & 0 deletions src/components/TabView/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Card, Text } from "@stellar/design-system";
import { WithInfoText } from "@/components/WithInfoText";
import { Tabs } from "@/components/Tabs";
import "./styles.scss";

type Tab = {
id: string;
label: string;
content: React.ReactNode;
};

type TabViewProps = {
heading: TabViewHeadingProps;
tab1: Tab;
tab2: Tab;
tab3?: Tab;
tab4?: Tab;
tab5?: Tab;
tab6?: Tab;
onTabChange: (id: string) => void;
activeTabId: string;
staticTop?: React.ReactNode;
};

export const TabView = ({
heading,
onTabChange,
activeTabId,
staticTop,
...tabs
}: TabViewProps) => {
const tabItems = Object.values(tabs).map((t) => ({
id: t.id,
label: t.label,
}));

const tabContent = Object.values(tabs).map((t) => ({
id: t.id,
content: t.content,
}));

return (
<div className="TabView">
<div className="TabView__heading">
<TabViewHeading {...heading} />

<div className="TabView__tabContainer">
<Tabs
tabs={tabItems}
activeTabId={activeTabId}
onChange={onTabChange}
/>
</div>
</div>

<Card>
<div className="TabView__body">
{staticTop ?? null}

<div className="TabView__content">
{tabContent.map((tc) => (
<div key={tc.id} data-is-active={activeTabId === tc.id}>
{tc.content}
</div>
))}
</div>
</div>
</Card>
</div>
);
};

type TabViewHeadingProps = (
| {
infoText: React.ReactNode | string;
href?: undefined;
}
| {
infoText?: undefined;
href: string;
}
| { infoText?: undefined; href?: undefined }
) & {
title: string;
infoHoverText?: string;
};

const TabViewHeading = ({
title,
infoHoverText,
infoText,
href,
}: TabViewHeadingProps) => {
const renderTitle = () => (
<Text size="md" as="h1" weight="medium">
{title}
</Text>
);

if (href || infoText) {
if (href) {
return (
<WithInfoText href={href} infoHoverText={infoHoverText}>
{renderTitle()}
</WithInfoText>
);
}

return (
<WithInfoText infoText={infoText} infoHoverText={infoHoverText}>
{renderTitle()}
</WithInfoText>
);
}

return renderTitle();
};
30 changes: 30 additions & 0 deletions src/components/TabView/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@use "../../styles/utils.scss" as *;

.TabView {
display: flex;
flex-direction: column;
gap: pxToRem(12px);

&__heading {
display: flex;
justify-content: space-between;
align-items: center;
gap: pxToRem(24px);
}

&__body {
display: flex;
flex-direction: column;
gap: pxToRem(12px);
}

&__content {
& > [data-is-active="false"] {
display: none;
}

& > [data-is-active="true"] {
display: block;
}
}
}
31 changes: 31 additions & 0 deletions src/components/Tabs/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import "./styles.scss";

type Tab = {
id: string;
label: string;
};

export const Tabs = ({
tabs,
activeTabId,
onChange,
}: {
tabs: Tab[];
activeTabId: string;
onChange: (id: string) => void;
}) => {
return (
<div className="Tabs">
{tabs.map((t) => (
<div
key={t.id}
className="Tab"
data-is-active={t.id === activeTabId}
onClick={() => onChange(t.id)}
>
{t.label}
</div>
))}
</div>
);
};
Loading

0 comments on commit b1d6fdf

Please sign in to comment.