Skip to content

Commit 796de27

Browse files
authored
Merge pull request linode#28 from linode/DLC-161
DLC-161 Add a Back to top button for mobile on API Docs
2 parents 7bb62e7 + 25cc58a commit 796de27

File tree

3 files changed

+287
-191
lines changed

3 files changed

+287
-191
lines changed

src/components/5_templates/api.js

Lines changed: 185 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -17,172 +17,207 @@ import Security from "../../components/2_molecules/Security";
1717
import ResponseList from "../../components/2_molecules/ResponseList";
1818
import ResponseSamples from "../2_molecules/ResponseSamples";
1919

20+
import Caret from "../../images/svgs/caret.svg";
2021
import Copy from "../../images/svgs/copy.svg";
2122

22-
const apiPage = ({ data }) => {
23-
const n = data.allPaths.edges[0].node;
24-
const modes = {
25-
get: "get",
26-
put: "put",
27-
post: "post",
28-
delete: "delete"
29-
};
30-
const responseOptions = {
31-
_200: "_200",
32-
_204: "_204",
33-
default: "default"
34-
};
35-
return (
36-
<Layout fullWidth>
37-
<SEO
38-
title={
39-
"Linode API | " + (n.get && n.get.tags) ||
40-
(n.post && n.post.tags) ||
41-
(n.put && n.put.tags) ||
42-
(n.delete && n.delete.tags)
43-
}
44-
description={
45-
(n.get && n.get.description ? n.get.description : "") +
46-
(n.post && n.post.description ? n.post.description : "") +
47-
(n.put && n.put.description ? n.put.description : "") +
48-
(n.delete && n.delete.description ? n.delete.description : "")
49-
}
50-
/>
51-
<div className="flex flex-wrap">
52-
<div className="md:hidden search-header-wrapper">
53-
<SearchHeader />
54-
</div>
55-
<div className="sidebar-container">
56-
<Sidebar />
57-
</div>
58-
<div className="w-full px-4 api-content-wrapper">
59-
<div className="api-content mx-auto">
60-
<h1 className="mb-4 ">
61-
{(n.get && n.get.tags) ||
62-
(n.post && n.post.tags) ||
63-
(n.put && n.put.tags) ||
64-
(n.delete && n.delete.tags)}
65-
</h1>
66-
{Object.keys(n).map((e, i) => {
67-
const mode = modes[e];
68-
const m = n[mode];
23+
const scrollToTop = () => {
24+
window.scrollTo({
25+
top: 0,
26+
left: 0,
27+
behavior: "smooth"
28+
});
29+
};
6930

70-
return (
71-
m && (
72-
<div key={i} className="flex flex-col">
73-
<span id={mode} className="endpoint-anchor" />
74-
<div className="xs-full mb-8">
75-
<h2 className="mt-0">{m.summary}</h2>
76-
<div className="bg-ThemeCell p-4 mt-4 mb-8 flex items-center justify-between flex-wrap">
77-
<div className="flex items-center mr-4">
78-
<span className="tag big bold mr-2 uppercase">
79-
{mode}
80-
</span>
81-
<pre className="whitespace-pre-line">
82-
{m.servers
83-
? m.servers[0].url
84-
: "https://api.linode.com/v4"}
85-
{n.name}
86-
</pre>
87-
</div>
88-
{m.servers &&
89-
m.servers[0].url ===
90-
"https://api.linode.com/v4beta" && (
91-
<span className="tag tag-beta">BETA</span>
92-
)}
93-
</div>
94-
<Markdown
95-
source={m.description}
96-
escapeHtml={false}
97-
className="my-8 api-desc"
98-
/>
99-
{m.security && <Security oauth={m.security[1].oauth} />}
100-
{m.parameters && (
101-
<div className="my-8">
102-
<h3 className="mb-2">Query Parameters</h3>
103-
{m.parameters.map((param, i) => (
104-
<ParamDisplay
105-
key={`param-item-${i}`}
106-
param={param}
107-
m={m}
108-
/>
109-
))}
110-
</div>
111-
)}
112-
{n.parameters && (
113-
<div className="my-8">
114-
<h3 className="mb-2">Path Parameters</h3>
115-
{n.parameters.map((param, i) => (
116-
<ParamDisplay
117-
key={`param-item-${i}`}
118-
param={param}
119-
m={n}
120-
/>
121-
))}
31+
class apiPage extends React.Component {
32+
componentDidMount() {
33+
window.addEventListener("scroll", () => {
34+
const top = window.scrollY;
35+
const scrollButton = document.getElementById("back-to-top");
36+
if (top >= 50) {
37+
scrollButton.classList.add("is-visible");
38+
} else {
39+
scrollButton.classList.remove("is-visible");
40+
}
41+
});
42+
}
43+
44+
render() {
45+
const n = this.props.data.allPaths.edges[0].node;
46+
const modes = {
47+
get: "get",
48+
put: "put",
49+
post: "post",
50+
delete: "delete"
51+
};
52+
const responseOptions = {
53+
_200: "_200",
54+
_204: "_204",
55+
default: "default"
56+
};
57+
58+
return (
59+
<Layout fullWidth>
60+
<SEO
61+
title={
62+
"Linode API | " + (n.get && n.get.tags) ||
63+
(n.post && n.post.tags) ||
64+
(n.put && n.put.tags) ||
65+
(n.delete && n.delete.tags)
66+
}
67+
description={
68+
(n.get && n.get.description ? n.get.description : "") +
69+
(n.post && n.post.description ? n.post.description : "") +
70+
(n.put && n.put.description ? n.put.description : "") +
71+
(n.delete && n.delete.description ? n.delete.description : "")
72+
}
73+
/>
74+
<div className="flex flex-wrap">
75+
<div className="md:hidden search-header-wrapper">
76+
<SearchHeader />
77+
</div>
78+
<div className="sidebar-container">
79+
<Sidebar />
80+
</div>
81+
<div className="w-full px-4 api-content-wrapper">
82+
<div className="api-content mx-auto">
83+
<h1 className="mb-4 ">
84+
{(n.get && n.get.tags) ||
85+
(n.post && n.post.tags) ||
86+
(n.put && n.put.tags) ||
87+
(n.delete && n.delete.tags)}
88+
</h1>
89+
{Object.keys(n).map((e, i) => {
90+
const mode = modes[e];
91+
const m = n[mode];
92+
93+
return (
94+
m && (
95+
<div key={i} className="flex flex-col">
96+
<span id={mode} className="endpoint-anchor" />
97+
<div className="xs-full mb-8">
98+
<h2 className="mt-0">{m.summary}</h2>
99+
<div className="bg-ThemeCell p-4 mt-4 mb-8 flex items-center justify-between flex-wrap">
100+
<div className="flex items-center mr-4">
101+
<span className="tag big bold mr-2 uppercase">
102+
{mode}
103+
</span>
104+
<pre className="whitespace-pre-line">
105+
{m.servers
106+
? m.servers[0].url
107+
: "https://api.linode.com/v4"}
108+
{n.name}
109+
</pre>
110+
</div>
111+
{m.servers &&
112+
m.servers[0].url ===
113+
"https://api.linode.com/v4beta" && (
114+
<span className="tag tag-beta">BETA</span>
115+
)}
122116
</div>
123-
)}
124-
{m.requestBody && <BodySchema data={m} />}
125-
<div className="w-full mb-8">
126-
<h3>Request Samples</h3>
127-
<Tabs className="my-4">
128-
<TabList>
117+
<Markdown
118+
source={m.description}
119+
escapeHtml={false}
120+
className="my-8 api-desc"
121+
/>
122+
{m.security && <Security oauth={m.security[1].oauth} />}
123+
{m.parameters && (
124+
<div className="my-8">
125+
<h3 className="mb-2">Query Parameters</h3>
126+
{m.parameters.map((param, i) => (
127+
<ParamDisplay
128+
key={`param-item-${i}`}
129+
param={param}
130+
m={m}
131+
/>
132+
))}
133+
</div>
134+
)}
135+
{n.parameters && (
136+
<div className="my-8">
137+
<h3 className="mb-2">Path Parameters</h3>
138+
{n.parameters.map((param, i) => (
139+
<ParamDisplay
140+
key={`param-item-${i}`}
141+
param={param}
142+
m={n}
143+
/>
144+
))}
145+
</div>
146+
)}
147+
{m.requestBody && <BodySchema data={m} />}
148+
<div className="w-full mb-8">
149+
<h3>Request Samples</h3>
150+
<Tabs className="my-4">
151+
<TabList>
152+
{m.x_code_samples &&
153+
m.x_code_samples.map((x, i) => {
154+
return <Tab key={i}>{x.lang}</Tab>;
155+
})}
156+
</TabList>
129157
{m.x_code_samples &&
130158
m.x_code_samples.map((x, i) => {
131-
return <Tab key={i}>{x.lang}</Tab>;
132-
})}
133-
</TabList>
134-
{m.x_code_samples &&
135-
m.x_code_samples.map((x, i) => {
136-
return (
137-
<TabPanel key={i}>
138-
<div className="flex justify-end text-sm">
139-
<Clipboard
140-
data-clipboard-text={x.source}
141-
className="flex items-center hover:text-BaseBlueLight"
159+
return (
160+
<TabPanel key={i}>
161+
<div className="flex justify-end text-sm">
162+
<Clipboard
163+
data-clipboard-text={x.source}
164+
className="flex items-center hover:text-BaseBlueLight"
165+
>
166+
<span className="mr-2">Copy</span>
167+
<Copy
168+
style={{ width: 22, height: 22 }}
169+
/>
170+
</Clipboard>
171+
</div>
172+
<SyntaxHighlighter
173+
language="bash"
174+
style={atomDark}
175+
className="api-samples"
176+
codeTagProps={{
177+
style: { whiteSpace: "pre-wrap" }
178+
}}
142179
>
143-
<span className="mr-2">Copy</span>
144-
<Copy style={{ width: 22, height: 22 }} />
145-
</Clipboard>
146-
</div>
147-
<SyntaxHighlighter
148-
language="bash"
149-
style={atomDark}
150-
className="api-samples"
151-
codeTagProps={{
152-
style: { whiteSpace: "pre-wrap" }
153-
}}
154-
>
155-
{x.source}
156-
</SyntaxHighlighter>
157-
</TabPanel>
158-
);
159-
})}
160-
</Tabs>
161-
<ResponseSamples
180+
{x.source}
181+
</SyntaxHighlighter>
182+
</TabPanel>
183+
);
184+
})}
185+
</Tabs>
186+
<ResponseSamples
187+
options={responseOptions}
188+
responses={m.responses}
189+
m={m}
190+
mode={mode}
191+
/>
192+
</div>
193+
194+
<ResponseList
162195
options={responseOptions}
163196
responses={m.responses}
164197
m={m}
165198
mode={mode}
166199
/>
167200
</div>
168-
169-
<ResponseList
170-
options={responseOptions}
171-
responses={m.responses}
172-
m={m}
173-
mode={mode}
174-
/>
175201
</div>
176-
</div>
177-
)
178-
);
179-
})}
202+
)
203+
);
204+
})}
205+
</div>
180206
</div>
181207
</div>
182-
</div>
183-
</Layout>
184-
);
185-
};
208+
<div
209+
className="back-to-top md:hidden"
210+
onClick={scrollToTop}
211+
id="back-to-top"
212+
>
213+
<span className="back-to-top__caret">
214+
<Caret />
215+
</span>
216+
</div>
217+
</Layout>
218+
);
219+
}
220+
}
186221

187222
export default apiPage;
188223

src/css/components/4_pages/api-page.css

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,31 @@
191191
box-shadow: 0px 5px 8px -2px rgba(0, 0, 0, 0.5);
192192
}
193193

194+
.back-to-top {
195+
background-color: #f2f2f2;
196+
color: #000000;
197+
cursor: pointer;
198+
position: fixed;
199+
right: 20px;
200+
display: none;
201+
z-index: 100;
202+
border-radius: 50%;
203+
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
204+
bottom: 20px;
205+
width: 60px;
206+
height: 60px;
207+
padding: 0 20px;
208+
outline: 0 !important;
209+
}
210+
211+
.is-visible {
212+
display: block;
213+
}
214+
215+
.back-to-top__caret svg {
216+
transform: rotate(270deg);
217+
}
218+
194219
@screen md {
195220
.search-header .search-results-list {
196221
max-height: none;

0 commit comments

Comments
 (0)