Skip to content

Commit

Permalink
Expose dotcom shared components (primer#557)
Browse files Browse the repository at this point in the history
* Expose dotcom shared components

* Update content/github-staff/github-shared-components.mdx

Co-authored-by: Emily Brick <emilybrick@github.com>

* Change format of shared_components.json

* Renaming

* Fix link styles; rearrange shared components page

* Stop using outdated shared deploy config

* Add ListView component to fallback recipe file

* Pull data from github master branch

* Remove unnecessary console.log; remove unused ToC item

* D'oh, fix other ToC link

* Stop grouping shared components alphabetically

---------

Co-authored-by: Emily Brick <emilybrick@github.com>
camertron and emilybrick authored Sep 1, 2023
1 parent ef0b103 commit f22d3b1
Showing 11 changed files with 717 additions and 74,541 deletions.
72 changes: 62 additions & 10 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ on:
# Manual deploy
workflow_dispatch:
# Scheduled deploy
schedule:
schedule:
- cron: '0 0 * * *' # Every day at midnight UTC time
# Deploy on change to main
push:
@@ -17,13 +17,65 @@ permissions:
id-token: write

jobs:
build_deploy:
build:
name: Build
runs-on: ubuntu-latest
steps:
- id: get-dotcom-access-token
uses: camertron/github-app-installation-auth-action@v1
with:
app-id: ${{ vars.DOTCOM_SHARED_COMPONENTS_APP_ID }}
private-key: ${{ secrets.DOTCOM_SHARED_COMPONENTS_APP_PRIVATE_KEY }}
client-id: ${{ vars.DOTCOM_SHARED_COMPONENTS_APP_CLIENT_ID }}
client-secret: ${{ secrets.DOTCOM_SHARED_COMPONENTS_APP_CLIENT_SECRET }}
installation-id: ${{ vars.DOTCOM_SHARED_COMPONENTS_APP_INSTALLATION_ID }}

- name: Checkout default branch
uses: actions/checkout@v2
with:
version: 16

- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: 16

- name: Cache dependencies
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: yarn

- name: Build
run: yarn build
env:
GITHUB_TOKEN: ${{ steps.get-dotcom-access-token.outputs.access-token }}

- name: Archive build output
run: "tar --dereference --directory public -cvf artifact.tar ."

- name: Upload artifact
uses: actions/upload-artifact@main
with:
name: github-pages
path: artifact.tar

deploy:
if: ${{ github.repository == 'primer/design' }}
name: Production
# SHA for security hardening. Points at last verified HEAD of main branch.
uses: primer/.github/.github/workflows/deploy.yml@0cec9b9914f358846163f2428663b58da41028c9
with:
node_version: 16
install: yarn
build: yarn build
output_dir: public
name: Deploy
runs-on: ubuntu-latest
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
with:
preview: false
76 changes: 66 additions & 10 deletions .github/workflows/deploy_preview.yml
Original file line number Diff line number Diff line change
@@ -9,15 +9,71 @@ permissions:
id-token: write

jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- id: get-dotcom-access-token
uses: camertron/github-app-installation-auth-action@v1
with:
app-id: ${{ vars.DOTCOM_SHARED_COMPONENTS_APP_ID }}
private-key: ${{ secrets.DOTCOM_SHARED_COMPONENTS_APP_PRIVATE_KEY }}
client-id: ${{ vars.DOTCOM_SHARED_COMPONENTS_APP_CLIENT_ID }}
client-secret: ${{ secrets.DOTCOM_SHARED_COMPONENTS_APP_CLIENT_SECRET }}
installation-id: ${{ vars.DOTCOM_SHARED_COMPONENTS_APP_INSTALLATION_ID }}

- name: Checkout default branch
uses: actions/checkout@v2
with:
version: 16

- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: 16

- name: Cache dependencies
uses: actions/cache@v2
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: yarn

- name: Build
run: yarn build:preview
env:
GITHUB_TOKEN: ${{ steps.get-dotcom-access-token.outputs.access-token }}

- name: Archive build output
run: "tar --dereference --directory public -cvf artifact.tar ."

- name: Upload artifact
uses: actions/upload-artifact@main
with:
name: github-pages
path: artifact.tar

deploy-preview:
if: ${{ github.event.pull_request.head.repo.full_name == 'primer/design' }}
if: ${{ github.repository == 'primer/design' }}
name: Preview
# SHA for security hardening. Points at last verified HEAD of main branch.
uses: primer/.github/.github/workflows/deploy_preview.yml@0cec9b9914f358846163f2428663b58da41028c9
secrets:
gh_token: ${{ secrets.GITHUB_TOKEN }}
with:
node_version: 16
install: yarn
build: yarn build:preview
output_dir: public
runs-on: ubuntu-latest
needs: build
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
permissions:
contents: read
pages: write
id-token: write
outputs:
deployment_url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
with:
preview: true
17 changes: 10 additions & 7 deletions content/github-staff/github-shared-components.mdx
Original file line number Diff line number Diff line change
@@ -3,21 +3,24 @@ title: GitHub shared components
description: Application-specific components that are shared by GitHub feature teams but are not in Primer.
---

import DotcomSharedComponentsLayout from '~/src/layouts/dotcom-shared-components-layout'
export default DotcomSharedComponentsLayout

<Note>This information is only relevant to GitHub staff.</Note>

These components are shared design patterns by GitHub feature teams developing application-specific components. We encourage relying only on Primer components where possible, and not all patterns will be upstreamed to Primer.
These components are shared design patterns by GitHub feature teams developing application-specific components. We encourage relying only on Primer components where possible, and not all patterns will be upstreamed to Primer.

They're shared with other teams by developing them as React components within the `ui/packages/` monorepo. This monorepo, along with the larger monolith, provides a solid foundation with baseline configurations for linting, accessibility scanning using Axe, and Storybook previews.

Engineers building UI components should refer to the guidance on The Hub: [Building reusable UI Components](https://thehub.github.com/epd/engineering/dev-practicals/frontend/common-components/) (only available to GitHub staff).

## Finding these components

GitHub shared components are documented in a private Storybook instance: [ui/packages/ Storybook](https://gh.io/storybook) (only available to GitHub staff).

Please note that these components are **owned by the feature teams**, and will not exist in our Figma component library, [Primer Web](https://www.figma.com/file/GCvY3Qv8czRgZgvl1dG6lp/Primer-Web?type=design&node-id=1406%3A0&mode=design&t=Qi8hXoRhKLLrDhgf-1), unless they have been upstreamed into Primer.

## Related reading

- [Handling new patterns](/guides/contribute/handling-new-patterns)
- [Upstreaming to Primer](/guides/contribute/adding-new-components#upstreaming-to-primer)

## Shared components

GitHub shared components are documented in a private Storybook instance: [ui/packages/ Storybook](https://gh.io/storybook) (only available to GitHub staff).

Please note that these components are **owned by the feature teams**, and will not exist in our Figma component library, [Primer Web](https://www.figma.com/file/GCvY3Qv8czRgZgvl1dG6lp/Primer-Web?type=design&node-id=1406%3A0&mode=design&t=Qi8hXoRhKLLrDhgf-1), unless they have been upstreamed into Primer.
138 changes: 138 additions & 0 deletions gatsby-node.esm.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,10 @@ import * as path from 'path'
import * as fs from 'fs'
import * as defines from './babel-defines'
import fetch from 'node-fetch'
import GithubSlugger from 'github-slugger'
import { latestStatusFrom } from './src/rails-status'
import { Octokit } from '@octokit/rest'
import JSZip from 'jszip'

exports.onCreateWebpackConfig = ({actions, plugins, getConfig}) => {
const config = getConfig()
@@ -32,6 +35,141 @@ exports.sourceNodes = async ({actions, createNodeId, createContentDigest}) => {
await sourcePrimerRailsData({actions, createNodeId, createContentDigest})
await sourceOcticonData({actions, createNodeId, createContentDigest})
await sourceFigmaData({actions, createNodeId, createContentDigest})
await sourceDotcomSharedComponentsData({actions, createNodeId, createContentDigest})
}

exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type SharedComponent implements Node {
component: String!
storyIds: [String!]
status: String!
path: String!
}
`
createTypes(typeDefs)
}

async function sourceDotcomSharedComponentsData({actions, createNodeId, createContentDigest}) {
const sharedComponents = await getSharedComponentsData()
const slugger = new GithubSlugger()

for (const sharedComponent of sharedComponents) {
const {component: name} = sharedComponent

const newNode = {
component: sharedComponent.component,
storyIds: sharedComponent.storyIds,
status: sharedComponent.meta.status,
path: sharedComponent.meta.path,
id: createNodeId(`dotcom-shared-${name}`),
internal: {
type: 'SharedComponent',
contentDigest: createContentDigest(sharedComponent),
},
}

actions.createNode(newNode)

const sharedComponentsPath = '/github-staff/github-shared-components'
const componentPath = `${sharedComponentsPath}/${slugger.slug(name)}`

actions.createRedirect({
fromPath: componentPath,
toPath: `${sharedComponentsPath}#${name[0].toLowerCase()}`,
redirectInBrowser: true,
force: true
})

const searchDoc = {
title: name,
path: componentPath,
rawBody: name
}

const searchNode = {
...searchDoc,
id: createNodeId(`shared-component-search-doc-${name}`),
internal: {
type: 'CustomSearchDoc',
contentDigest: createContentDigest(searchDoc)
}
}

actions.createNode(searchNode)
}
}

async function getSharedComponentsData() {
if (!process.env.GITHUB_TOKEN) {
console.log('No GITHUB_TOKEN in environment, falling back to using recipe_metadata.json')
return JSON.parse(fs.readFileSync('recipe_metadata.json', 'utf8'))
}

const client = new Octokit({
auth: process.env.GITHUB_TOKEN
})

console.log('Listing dotcom workflow runs...')

const workflowRuns = await client.rest.actions.listWorkflowRuns({
owner: 'github',
repo: 'github',
workflow_id: 'preview-pages-build.yml',
branch: 'master',
status: 'success',
per_page: 1
})

if (workflowRuns.data.workflow_runs.length == 0) {
console.log('No workflow runs found 🤔')
return
}

const workflowRunId = workflowRuns.data.workflow_runs[0].id
console.log(`Workflow run ${workflowRunId} found`)

console.log('Listing workflow run artifacts...')
const artifacts = await client.rest.actions.listWorkflowRunArtifacts({
owner: 'github',
repo: 'github',
run_id: workflowRunId
})

const artifactId = (() => {
for (const artifact of artifacts.data.artifacts) {
if (artifact.name == 'recipe-metadata') {
return artifact.id
}
}

return null
})()

if (artifactId) {
console.log(`Found artifact ${artifactId}`)
} else {
console.log('No artifacts found for workflow run 🤯')
return
}

console.log('Downloading artifact...')
const artifactContents = await client.rest.actions.downloadArtifact({
owner: 'github',
repo: 'github',
artifact_id: artifactId,
archive_format: 'zip'
})

console.log('Extracting artifact...')

const zip = new JSZip();
const zipData = await zip.loadAsync(artifactContents.data)
const manifestRaw = await zipData.file('recipe_metadata.json').async('string')
const manifest = JSON.parse(manifestRaw)

return manifest
}

async function sourcePrimerRailsData({actions, createNodeId, createContentDigest}) {
74,509 changes: 0 additions & 74,509 deletions package-lock.json

This file was deleted.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -14,6 +14,8 @@
},
"dependencies": {
"@github/prettier-config": "^0.0.6",
"@octokit/auth-app": "^4.0.13",
"@octokit/rest": "^19.0.13",
"@primer/component-metadata": "^0.5.1",
"@primer/gatsby-theme-doctocat": "^4.7.1",
"@primer/octicons-react": "^17.3.0",
@@ -31,6 +33,7 @@
"gatsby-plugin-svgr": "^3.0.0-beta.0",
"gatsby-plugin-typescript": "^2.12.1",
"jspdf": "^2.5.1",
"jszip": "^3.10.1",
"node-fetch": "^2.6.12",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -42,8 +45,8 @@
"yarn": "^1.22.18"
},
"devDependencies": {
"esm": "^3.2.25",
"@github/markdownlint-github": "^0.3.0",
"esm": "^3.2.25",
"markdownlint-cli2": "^0.5.1",
"markdownlint-cli2-formatter-pretty": "^0.0.3",
"mustache": "^4.2.0",
25 changes: 25 additions & 0 deletions recipe_metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
[
{
"component": "ItemPicker",
"storyIds": [],
"meta": {
"id": "item-picker",
"name": "Item picker",
"path": "ui/packages/item-picker/components/ItemPicker.tsx",
"status": "draft"
}
},
{
"component": "ListView",
"storyIds": [
"shared-components-listview--docs",
"shared-components-listview--list"
],
"meta": {
"id": "list-view",
"name": "List view",
"path": "ui/packages/list-view/src/ListView.tsx",
"status": "draft"
}
}
]
1 change: 0 additions & 1 deletion src/layouts/css-component-layout.tsx
Original file line number Diff line number Diff line change
@@ -31,7 +31,6 @@ export const query = graphql`
`

export default function CssComponentLayout({data}) {
console.log(data.sitePage)
const name = data.sitePage?.context.frontmatter.cssId || ''
const title = data.sitePage?.context.frontmatter.title || name
const description = data.sitePage?.context.frontmatter.description || ''
102 changes: 102 additions & 0 deletions src/layouts/dotcom-shared-components-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import {HEADER_HEIGHT} from '@primer/gatsby-theme-doctocat/src/components/header'
import TableOfContents from '@primer/gatsby-theme-doctocat/src/components/table-of-contents'
import {Box, Heading, Label, Link, Text} from '@primer/react'
import React from 'react'
import {BaseLayout} from '../components/base-layout'
import {graphql, useStaticQuery} from 'gatsby'
import { H3 } from '@primer/gatsby-theme-doctocat/src/components/heading'
import {LinkExternalIcon} from '@primer/octicons-react'

function SharedComponentStatusLabel({status}) {
const variant = (() => {
if (status === 'draft') {
return 'attention'
} else {
return 'default'
}
})()

return <Label sx={{marginLeft: '5px'}} variant={variant}>{status}</Label>
}

function SharedComponentLink({component}) {
const urlForComponent = (component) => {
return `https://ui.githubapp.com/storybook/?path=/story/${component.storyIds[0]}`
}

if (component.storyIds.length > 0) {
return <Box>
<Link target="_blank" href={urlForComponent(component)}>
{component.component} <LinkExternalIcon/>
</Link>
&nbsp;
<SharedComponentStatusLabel status={component.status}></SharedComponentStatusLabel>
</Box>
} else {
return <Box>
<Link target="_blank" href={`https://github.com/github/github/blob/master/${component.path}`}>
{component.component} <LinkExternalIcon/>
</Link>
<SharedComponentStatusLabel status={component.status}></SharedComponentStatusLabel>
</Box>
}
}

export default function DotcomSharedComponentsLayout({pageContext, children}) {
const data = useStaticQuery(graphql`
query DotcomSharedComponentsPageQuery {
allSharedComponent {
nodes {
component
storyIds
status
path
}
}
}
`)

const {title, description} = pageContext.frontmatter

const tableOfContents = {
items: [
{url: '#related-reading', title: 'Related reading'},
{url: '#shared-components', title: 'Components'}
],
}

return (
<BaseLayout title={title} description={description}>
<Box sx={{maxWidth: 1200, width: '100%', p: [4, 5, 6, 7], mx: 'auto'}}>
<Heading as="h1" sx={{fontSize: 7}}>{title}</Heading>
<Text as="p" sx={{fontSize: 3, m: 0, mb: 3, maxWidth: '60ch'}}>
{description}
</Text>
<Box sx={{display: 'flex', flexDirection: 'row-reverse', alignItems: 'start', gap: [null, 7, 8, 9]}}>
<Box
sx={{
width: 220,
flex: '0 0 auto',
position: 'sticky',
top: HEADER_HEIGHT + 24,
maxHeight: `calc(100vh - ${HEADER_HEIGHT}px - 24px)`,
display: ['none', null, 'block'],
}}
>
<Heading as="h3" sx={{fontSize: 1, display: 'inline-block', fontWeight: 'bold', pl: 3}} id="toc-heading">
On this page
</Heading>
<TableOfContents aria-labelledby="toc-heading" items={tableOfContents.items} />
</Box>
<Box sx={{'flexGrow': 1}}>
{children}

{data.allSharedComponent.nodes.map(component => {
return <SharedComponentLink component={component} />
})}
</Box>
</Box>
</Box>
</BaseLayout>
)
}
1 change: 0 additions & 1 deletion src/layouts/figma-component-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {Note, StatusLabel} from '@primer/gatsby-theme-doctocat'
import {LinkExternalIcon} from '@primer/octicons-react'
import {HEADER_HEIGHT} from '@primer/gatsby-theme-doctocat/src/components/header'
import {Box, Heading, Label, Link, Text} from '@primer/react'
import FigmaLink from '@primer/gatsby-theme-doctocat/src/components/figma-link'
312 changes: 310 additions & 2 deletions yarn.lock

Large diffs are not rendered by default.

0 comments on commit f22d3b1

Please sign in to comment.