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

Add playground pages (instruqt sandbox) to Terraform and Vault #2675

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,13 @@ export function getNavItems(currentProduct: ProductData): NavItem[] {
}
}

if (currentProduct.playgroundConfig?.labs?.length) {
items.push({
label: 'Playground',
url: `/${currentProduct.slug}/playground`,
})
}

/**
* For Terraform, add a "Registry" item
*/
Expand Down
36 changes: 23 additions & 13 deletions src/contexts/instruqt-lab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ interface InstruqtContextProps {
interface InstruqtProviderProps {
labId: string
children: ReactNode
defaultActive?: boolean
isPlayground?: boolean
}

const InstruqtContext = createContext<Partial<InstruqtContextProps>>({})
Expand All @@ -34,23 +36,31 @@ export const useInstruqtEmbed = (): Partial<InstruqtContextProps> =>
export default function InstruqtProvider({
labId,
children,
defaultActive = false,
isPlayground = false,
}: InstruqtProviderProps): JSX.Element {
const [active, setActive] = useState(false)
const [active, setActive] = useState(defaultActive)

return (
<InstruqtContext.Provider value={{ labId, active, setActive }}>
{children}
{active && (
<div id="instruqt-panel-target">
<Resizable
initialHeight={640}
panelActive={active}
setPanelActive={setActive}
style={{ top: '-28px' }}
>
<EmbedElement />
</Resizable>
</div>
{isPlayground ? (
children
) : (
<>
{children}
{active && (
<div id="instruqt-panel-target">
<Resizable
initialHeight={640}
panelActive={active}
setPanelActive={setActive}
style={{ top: '-28px' }}
>
<EmbedElement />
</Resizable>
</div>
)}
</>
)}
</InstruqtContext.Provider>
)
Expand Down
27 changes: 27 additions & 0 deletions src/data/nomad.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,32 @@
],
"integrationsConfig": {
"description": "A curated collection of official, partner, and community Nomad Integrations."
},

"playgroundConfig": {
"description": "Learn how to manage your workloads with Nomad.",
"sidebarLinks": [
{
"title": "Install Nomad",
"href": "/nomad/install"
},
{
"title": "Get Started with Nomad",
"href": "/nomad/tutorials/get-started"
},
{
"title": "Nomad documentation",
"href": "/nomad/docs"
}
],
"labs": [
{
"id": "nomad-sandbox",
"name": "Nomad sandbox",
"instruqtId": "hashicorp-learn/tracks/nomad-sandbox?token=em_0wOuIAyyjAQllLkc",
"description": "A Nomad cluster with three server nodes and one client node, Consul installed and configured, and Access Control Lists (ACLs) enabled for both Nomad and Consul.",
"products": ["nomad", "consul"]
}
]
}
}
23 changes: 23 additions & 0 deletions src/data/playground-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"terraform": {
"labId": "hashicorp-learn/tracks/terraform-build-your-first-configuration"
},
"vault": {
"labId": "hashicorp-learn/tracks/vault-basics"
},
"consul": {
"labId": "hashicorp-learn/tracks/consul-template-automate-reverse-proxy-config"
},
"nomad": {
"labId": "hashicorp-learn/tracks/nomad-basics"
},
"packer": {
"labId": "hashicorp-learn/tracks/packer-get-started-hcp"
},
"waypoint": {
"labId": "hashicorp-learn/tracks/waypoint-get-started-docker"
},
"boundary": {
"labId": "hashicorp-learn/tracks/boundary-basics"
}
}
35 changes: 34 additions & 1 deletion src/data/terraform.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,38 @@
"path": "registry",
"productSlugForLoader": "terraform-docs-common"
}
]
],
"playgroundConfig": {
"description": "Learn how to create infrastructure with Terraform",
"sidebarLinks": [
{
"title": "Install Terraform",
"href": "/terraform/install"
},
{
"title": "Get Started with Terraform",
"href": "/terraform/tutorials/aws-get-started"
},
{
"title": "Terraform documentation",
"href": "/terraform/docs"
}
],
"labs": [
{
"id": "create-infrastructure",
"name": "Create Infrastructure",
"instruqtId": "hashicorp-learn/tracks/create-terraform-infrastructure?token=em__EA8k5ywxqiOejXd",
"description": "Learn how to create infrastructure with Terraform",
"products": ["terraform"]
},
{
"id": "vault-sandbox",
"name": "Vault Playground (test)",
"instruqtId": "hashicorp-learn/tracks/vault-sandbox?token=em_usmVkoZLWz8SAXNB",
"description": "Learn how to manage your secrets with Vault",
"products": ["vault"]
}
]
}
}
32 changes: 32 additions & 0 deletions src/data/vault.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,5 +174,37 @@
"href": "/vault/tutorials/custom-secrets-engine"
}
]
},
"playgroundConfig": {
"sidebarLinks": [
{
"title": "Install Vault",
"href": "/vault/install"
},
{
"title": "Get Started with Vault",
"href": "/vault/tutorials/get-started"
},
{
"title": "Vault Documentation",
"href": "/vault/docs"
}
],
"labs": [
{
"id": "vault-sandbox",
"name": "Vault Playground (test)",
"instruqtId": "hashicorp-learn/tracks/vault-sandbox?token=em_usmVkoZLWz8SAXNB",
"description": "Learn how to manage your secrets with Vault",
"products": ["vault"]
},
{
"id": "create-infrastructure",
"name": "Create Infrastructure",
"instruqtId": "hashicorp-learn/tracks/create-terraform-infrastructure?token=em__EA8k5ywxqiOejXd",
"description": "Learn how to create infrastructure with Terraform",
"products": ["terraform"]
}
]
}
}
181 changes: 181 additions & 0 deletions src/pages/[productSlug]/playground/[playgroundId].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import { GetStaticPaths, GetStaticProps } from 'next'
import { PRODUCT_DATA_MAP } from 'data/product-data-map'
import SidebarSidecarLayout from 'layouts/sidebar-sidecar'
import InstruqtProvider from 'contexts/instruqt-lab'
import EmbedElement from 'components/lab-embed/embed-element'
import {
generateTopLevelSidebarNavData,
generateProductLandingSidebarNavData,
} from 'components/sidebar/helpers'

interface PlaygroundPageProps {
product: (typeof PRODUCT_DATA_MAP)[keyof typeof PRODUCT_DATA_MAP]
playgroundId: string
playgroundName: string
playgroundDescription: string
layoutProps: {
breadcrumbLinks: { title: string; url: string }[]
navLevels: any[]
}
}

export default function PlaygroundView({
product,
playgroundId,
playgroundName,
playgroundDescription,
layoutProps,
}: PlaygroundPageProps) {
return (
<SidebarSidecarLayout
breadcrumbLinks={layoutProps.breadcrumbLinks}
sidebarNavDataLevels={layoutProps.navLevels}
>
<div>
<h1 className="g-type-display-3">{playgroundName}</h1>
<p className="g-type-body" style={{ marginTop: '16px' }}>
{playgroundDescription}
</p>
</div>
<div style={{ height: '80vh', marginTop: '32px' }}>
<InstruqtProvider labId={playgroundId} defaultActive isPlayground>
<EmbedElement />
</InstruqtProvider>
</div>
</SidebarSidecarLayout>
)
}

export const getStaticPaths: GetStaticPaths = async () => {
const paths = []

// Generate paths for each product's playgrounds
Object.values(PRODUCT_DATA_MAP).forEach((product) => {
if (product.playgroundConfig?.labs) {
product.playgroundConfig.labs.forEach((playground) => {
paths.push({
params: {
productSlug: product.slug,
playgroundId: playground.id,
},
})
})
}
})

return {
paths,
fallback: false,
}
}

export const getStaticProps: GetStaticProps<PlaygroundPageProps> = async ({
params,
}) => {
const productSlug = params?.productSlug as string
const playgroundId = params?.playgroundId as string
const product = PRODUCT_DATA_MAP[productSlug]

// Only show playground page if product has labs configured
if (!product || !product.playgroundConfig?.labs) {
return {
notFound: true,
}
}

const playground = product.playgroundConfig.labs.find(
(p) => p.id === playgroundId
)
if (!playground) {
return {
notFound: true,
}
}

const breadcrumbLinks = [
{ title: 'Developer', url: '/' },
{ title: product.name, url: `/${productSlug}` },
{ title: 'Playground', url: `/${productSlug}/playground` },
{
title: playground.name,
url: `/${productSlug}/playground/${playgroundId}`,
},
]

const sidebarNavDataLevels = [
generateTopLevelSidebarNavData(product.name),
generateProductLandingSidebarNavData(product),
]

// Add playground links
const playgroundMenuItems = [
{
title: `${product.name} Playground`,
fullPath: `/${productSlug}/playground`,
theme: product.slug,
isActive: false,
},
{
divider: true,
},
{
heading: 'Playgrounds',
},
...product.playgroundConfig.labs.map((p) => ({
title: p.name,
path: `/${productSlug}/playground/${p.id}`,
href: `/${productSlug}/playground/${p.id}`,
isActive: p.id === playgroundId,
})),
]

if (product.playgroundConfig.sidebarLinks) {
playgroundMenuItems.push(
{
divider: true,
},
{
heading: 'Resources',
},
...product.playgroundConfig.sidebarLinks.map((link) => ({
title: link.title,
path: link.href,
href: link.href,
isActive: false,
}))
)
}

sidebarNavDataLevels.push({
backToLinkProps: {
text: `${product.name} Home`,
href: `/${product.slug}`,
},
title: 'Playground',
menuItems: playgroundMenuItems,
showFilterInput: false,
visuallyHideTitle: true,
levelButtonProps: {
levelUpButtonText: `${product.name} Home`,
levelDownButtonText: 'Previous',
},
})

return {
props: {
product,
playgroundId: playground.instruqtId,
playgroundName: playground.name,
playgroundDescription: playground.description,
layoutProps: {
breadcrumbLinks,
navLevels: sidebarNavDataLevels,
},
},
}
}
Loading
Loading