Skip to content

Implement new accordion design for FAQ page #985

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

Merged
merged 24 commits into from
Dec 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
5 changes: 0 additions & 5 deletions src/assets/css/_css/docs.less
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,6 @@
}
}

.faq-questions::before {
content: "\A";
white-space: pre;
}

img {
max-width: 100%;
}
Expand Down
94 changes: 94 additions & 0 deletions src/assets/css/_css/faq.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
.inner-faq-content {
flex: 1;
@media screen and (max-width: 740px) {
margin-right: 0;
}

& > h1:first-child {
margin-top: 1em;
}

h2 {
margin-bottom: 10px;
}

.faq-button-question {
background: transparent;
display: block;
width: 100%;
text-align: left;
padding: 0;
cursor: pointer;
box-shadow: 0px -1px 0px #aaa;

&:first-child {
box-shadow: none;
}
}

h3 {
color: #e10098;
padding: 20px 0 20px 5px;
margin: 0;
user-select: none;
position: relative;

&.open:after {
transform: rotate(-45deg);
}

&:after {
content: "";
height: 33px;
width: 33px;
background-image: url(https://github.com/'data:image/svg+xml,%3Csvg%20xmlns="http://www.w3.org/2000/svg"%20viewBox="0%200%20100%20125"%3E%3Cpath%20height="20px"%20width="20px"%20style="text-indent:0;text-transform:none;block-progression:tb"%20d="M50%2013a4%204%200%2000-4%204v29H17a4%204%200%20000%208h29v29a4%204%200%20108%200V54h29a4%204%200%20100-8H54V17a4%204%200%2000-4-4z"%20overflow="visible"%20fill="%23aaa"/%3E%3C/svg%3E%0A');
background-repeat: no-repeat;
position: absolute;
transition: transform 200ms ease;
right: 0;

@media screen and (max-width: 768px) {
display: none;
}
}

a {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
}

ul {
max-height: 0;
opacity: 0;
margin-bottom: 0;
margin-top: 0;
display: none;

&.show {
opacity: 1;
max-height: 800px;
margin-bottom: 1.0875rem;
display: block;
}
}

p {
max-height: 0;
opacity: 0;
margin: 0;
display: none;

&.show {
opacity: 1;
max-height: 400px;
margin-bottom: 1.0875rem;
display: block;
}
}
}
1 change: 1 addition & 0 deletions src/assets/css/style.less
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
@import "_css/codemirror.less";
@import "_css/algolia.less";
@import "_css/code.less";
@import "_css/faq.less";
92 changes: 13 additions & 79 deletions src/components/FAQLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,94 +1,28 @@
import React from "react"
import { Link } from "gatsby"
import Marked from '../Marked'
import { toSlug } from '../../utils/slug'
import FAQSection from "../FAQSection"

interface Props {
title: string
gettingStartedQuestions: string
generalQuestions: string
bestPracticesQuestions: string
specificationQuestions: string
frontendQuestions: string
rawMarkdownBody: string
pageContext: any
}

const index = ({ title, gettingStartedQuestions, generalQuestions, bestPracticesQuestions, specificationQuestions, frontendQuestions, rawMarkdownBody }: Props) => {
const index = ({
title,
rawMarkdownBody,
pageContext
}: Props) => {
return (
<section>
<div className="documentationContent">
<div className="inner-content">
<h1>{title}</h1>
{gettingStartedQuestions && (
<div>
<h2>Getting Started</h2>
{gettingStartedQuestions
.split(',')
.map(gettingStartedQuestion => (
<Link className="faq-questions" key={gettingStartedQuestion} to={`#${toSlug( gettingStartedQuestion )}`}>
{gettingStartedQuestion}
</Link>
))
}
</div>
)}
{generalQuestions && (
<div>
<h2>General</h2>
{generalQuestions
.split(',')
.map(generalQuestion => (
<Link className="faq-questions" key={generalQuestion} to={`#${toSlug( generalQuestion )}`}>
{generalQuestion}
</Link>
))
}
</div>
)}
{bestPracticesQuestions && (
<div>
<h2>Best Practices</h2>
{bestPracticesQuestions
.split(',')
.map(bestPracticesQuestion => (
<Link className="faq-questions" key={bestPracticesQuestion} to={`#${toSlug( bestPracticesQuestion )}`}>
{bestPracticesQuestion}
</Link>
))
}
</div>
)}
{specificationQuestions && (
<div>
<h2>Specification</h2>
{specificationQuestions
.split(',')
.map(specificationQuestion => (
<Link className="faq-questions" key={specificationQuestion} to={`#${toSlug( specificationQuestion )}`}>
{specificationQuestion}
</Link>
))
}
</div>
)}
{frontendQuestions && (
<div>
<h2>Frontend</h2>
{frontendQuestions
.split(',')
.map(frontendQuestion => (
<Link className="faq-questions" key={frontendQuestion} to={`#${toSlug( frontendQuestion )}`}>
{frontendQuestion}
</Link>
))
}
</div>
)}
<Marked>{rawMarkdownBody}</Marked>
</div>
<FAQSection
title={title}
rawMarkdownBody={rawMarkdownBody}
pageContext={pageContext}
/>
</div>
</section>
)
}

export default index
export default index
21 changes: 21 additions & 0 deletions src/components/FAQSection/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react"
import Marked from "../Marked"

interface Props {
title: string
rawMarkdownBody: string
pageContext: any
}

const FAQSection = ({
title,
rawMarkdownBody,
pageContext
}: Props) => (
<section className="inner-faq-content">
<h2>{title}</h2>
<Marked pageContext={pageContext}>{rawMarkdownBody}</Marked>
</section>
)

export default FAQSection
60 changes: 60 additions & 0 deletions src/content/faq/BestPractices.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: Best Practices
layout: faq
permalink: /faq/best-practices
position: 3
---

### Is GraphQL scalable?

Yes, GraphQL is designed to be scalable and is used by many companies in production under a very high load.

GraphQL comes with some [built-in performance boosts](#how-does-graphql-affect-my-product-s-performance) that can help. But once you push it to production, you're responsible for scaling it across instances and monitoring performance.

### Does GraphQL support offline usage?

No, or at least not natively. But there are [GraphQL clients](#what-is-a-graphql-client-and-why-would-i-need-one) that enable you to build offline-first. They use features designed to perform data operations while offline, such as caching and service workers.

You can find a list of GraphQL clients in various languages on our [Code page](/code/).

### What are the security concerns with GraphQL?

Most of the security concerns associated with GraphQL are typical for any API or service. A few examples: SQL injections, Denial of Service (DoS) attacks, or someone abusing flawed authentication. But there are also some attacks specific to GraphQL. For instance, [batching attacks](https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html#batching-attacks). These attacks can happen as a result of GraphQL allowing you to batch multiple queries (or requests for multiple object instances) in a single network call.

No matter the concern, it’s important to be proactive. There are many ways to securing your GraphQL server. Using a timeout, setting a maximum depth for queries, and throttling queries based on the server time it needs to complete are all potential approaches.

For an overview of common security concerns and how to address them, check out the [Security tutorial on How to GraphQL](https://www.howtographql.com/advanced/4-security/) and [OWASP’s GraphQL Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html).

### How can I set up authorization with GraphQL?

We recommend enforcing authorization behavior in the [business logic layer](/learn/thinking-in-graphs/#business-logic-layer). That way, you have a single source of truth for authorization.

For a more detailed explanation, go to our [Authorization documentation](/learn/authorization/).

### How does authentication work with GraphQL?

You can implement authentication with common patterns, such as [OAuth](https://oauth.net/) or [JWT](https://jwt.io/). There’s nothing special about authentication within the GraphQL specification.

Some [GraphQL libraries](/code/#language-support) include a specific protocol for authentication as well. Although if you’re working with a pipeline model, we recommend that [GraphQL be placed after all authentication middleware](/learn/serving-over-http/#web-request-pipeline).

If you’re using [GraphQL.js](/graphql-js/) to build your API server, we have documentation on [handling authentication with Express middleware](/graphql-js/authentication-and-express-middleware/).

### Is GraphQL the right fit for designing a microservice architecture?

Yes, it can be. If you’re integrating GraphQL into your microservice architecture, we’d recommend having one GraphQL schema as an API gateway rather than having your client talk to multiple GraphQL services. This way, you can split your backend into microservices, but then still aggregate all your data to the frontend from a single API.

There are many ways to create an API gateway. The benefit of using GraphQL is that you can take advantage of features like [caching](/learn/caching/), request budgeting, and planning out query schedules.

### How does versioning work in GraphQL?

There’s nothing that will prevent a GraphQL service from being versioned like any other REST API. That said, GraphQL avoids versioning by design.

Instead, GraphQL provides the tools to continually build and evolve your schema. For example, GraphQL only returns the data that’s explicitly requested. This means that you can add new features (and all the associated types and fields) without creating a breaking change or bloating results for existing queries.

You can read more about [how versioning works in GraphQL](/learn/best-practices/#versioning) in our Best Practices section.

### How can I document my GraphQL API?

One of the benefits of GraphQL is that it's inherently self-documenting. This means that when you use an interactive tool like [GraphiQL](https://github.com/graphql/graphiql), you’re able to explore what data is exposed by your GraphQL API. This includes the [fields](/learn/queries/#fields), [types](/learn/schema/#type-system), and more. You can also add a [description field](https://spec.graphql.org/draft/#sec-Documentation) to provide supplementary notes about your endpoint. This description field supports strings and Markdown.

For many, this provides enough API reference documentation. But it doesn’t reduce the need for other forms of documentation. You'll likely still need to create guides that explain how the general concepts tie into your specific use case.
12 changes: 12 additions & 0 deletions src/content/faq/Frontend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Frontend
layout: faq
permalink: /faq/frontend
position: 5
---

### Does GraphQL replace Redux or other state management libraries?

No, GraphQL isn’t a state management library - but it can reduce the need for one.

One benefit of state management libraries like Redux is that they can manipulate API responses into a format that your application understands. With GraphQL, you have control over [what data you request](/learn/queries/#fields) and typically results are formatted in a client-friendly way by the graph design. So this benefit is already built-in. Many [client libraries](#what-is-a-graphql-client-and-why-would-i-need-one) can also be used to manage state and have features like caching built-in. You may still decide to implement a state management library, but using it to format response data is generally not necessary.
Loading