diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ae3ffc5fd..a0c45afbe 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -29,7 +29,6 @@ jobs: 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 @@ -53,6 +52,7 @@ jobs: run: yarn build env: GITHUB_TOKEN: ${{ steps.get-dotcom-access-token.outputs.access-token }} + FIGMA_API_TOKEN: ${{ secrets.FIGMA_TOKEN }} - name: Upload artifact uses: actions/upload-pages-artifact@v3 diff --git a/.github/workflows/deploy_preview.yml b/.github/workflows/deploy_preview.yml index 3ee032c8c..1bd02d528 100644 --- a/.github/workflows/deploy_preview.yml +++ b/.github/workflows/deploy_preview.yml @@ -48,6 +48,7 @@ jobs: run: yarn build:preview env: GITHUB_TOKEN: ${{ steps.get-dotcom-access-token.outputs.access-token }} + FIGMA_API_TOKEN: ${{ secrets.FIGMA_TOKEN }} - name: Upload artifact uses: actions/upload-pages-artifact@v2 diff --git a/.github/workflows/get_figma_images.yml b/.github/workflows/get_figma_images.yml deleted file mode 100644 index 63b055a7e..000000000 --- a/.github/workflows/get_figma_images.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Get Figma Images - -on: - push: - branches: - - main - pull_request: - types: - - labeled - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - findNodeReferences: - if: ${{ github.event.label.name == 'update figma images' || github.event_name == 'push' }} - runs-on: ubuntu-latest - env: - FilesToScan: '**/*.mdx' - ImageUrlFile: figmaImageNodeUrls.json - ImageOutputDir: content/images/figma - FigmaToken: ${{ secrets.FIGMA_TOKEN }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Set up Node - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: 'yarn' - - name: Install dependencies - run: yarn - - name: Get Figma Images - run: node scripts/getFigmaImages.js "${{env.FilesToScan}}" > ${{env.ImageUrlFile}} - - name: Log file content - run: cat ${{env.ImageUrlFile}} - - name: Download images from figma - run: npx @primer/figma-images --figmaToken ${{env.FigmaToken}} --nodeURLsFile ${{env.ImageUrlFile}} --outputDir ${{env.ImageOutputDir}} - - name: Log output dir content - run: ls ${{env.ImageOutputDir}} - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: github-actions[bot] Update figma images - - uses: actions-ecosystem/action-remove-labels@v1 - if: always() - with: - labels: 'update figma images' \ No newline at end of file diff --git a/README.md b/README.md index 7797d8c65..4085b5c7c 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,13 @@ This repository is evolving as our documentation needs change. See our [contribu 4. `yarn` to install dependencies 5. `yarn start` to start the dev server +### Figma API token + +To get images downloaded from the Figma API, you'll need to create a `.env` file in the root of the project and add your Figma API token. You can create a new token [here](https://www.figma.com/developers/api#access-tokens). + +```sh +`FIGMA_API_TOKEN=your-token-here` +`` ## Deployment diff --git a/content/components/banner.mdx b/content/components/banner.mdx index 2dcbbae50..514da9c9a 100644 --- a/content/components/banner.mdx +++ b/content/components/banner.mdx @@ -13,11 +13,12 @@ import {Box} from '@primer/react' import ComponentLayout from '~/src/layouts/component-layout' export default ComponentLayout import {AccessibilityLink} from '~/src/components/accessibility-link' +import {FigmaImage} from '~/src/components/figma-image' -Image displaying each variant of banner, from top: critical, warning, success, info, upsell. ## Usage diff --git a/package.json b/package.json index 1cbe23c21..9c83935ce 100644 --- a/package.json +++ b/package.json @@ -3,15 +3,15 @@ "repository": "primer/design", "version": "1.0.0", "scripts": { - "start": "gatsby develop", + "start": "yarn build:figma-images && gatsby develop", "clean": "gatsby clean", - "build": "npm run copy:figma-images && gatsby build", + "build": "yarn build:figma-images && gatsby build", + "build:figma-images": "node scripts/buildFigmaImages.mjs", + "build:preview": "yarn build:figma-images && gatsby build", "serve": "gatsby serve", - "build:preview": "npm run copy:figma-images && gatsby build", "now-build": "yarn build", "lint": "eslint .", - "markdownlint": "markdownlint-cli2 \"**/*.{md,mdx}\" \"!.github\" \"!node_modules\"", - "copy:figma-images": "mkdir -p public/images && cp -r content/images/figma public/images || true" + "markdownlint": "markdownlint-cli2 \"**/*.{md,mdx}\" \"!.github\" \"!node_modules\"" }, "dependencies": { "@github/prettier-config": "^0.0.6", @@ -21,8 +21,8 @@ "@primer/gatsby-theme-doctocat": "^4.7.1", "@primer/octicons-react": "^17.3.0", "@primer/react": "35.5.0", - "@styled-system/props": "^5.1.5", "@styled-system/prop-types": "5.1.5", + "@styled-system/props": "^5.1.5", "@styled-system/theme-get": "^5.1.2", "@svgr/webpack": "^6.5.1", "@tanstack/react-query": "^4.23.0", @@ -51,7 +51,8 @@ }, "devDependencies": { "@github/markdownlint-github": "^0.3.0", - "@primer/figma-images": "^0.1.2", + "@primer/figma-images": "^0.1.5", + "dotenv": "^16.4.5", "esm": "^3.2.25", "fast-glob": "^3.3.2", "markdownlint-cli2": "^0.5.1", diff --git a/scripts/buildFigmaImages.mjs b/scripts/buildFigmaImages.mjs new file mode 100644 index 000000000..c6893d65a --- /dev/null +++ b/scripts/buildFigmaImages.mjs @@ -0,0 +1,57 @@ +// Import necessary modules +import fs from 'fs/promises' +import fastGlob from 'fast-glob' +import {config} from 'dotenv' +import figmaImages from '@primer/figma-images' + +config() + +const fileGlob = '**/*.mdx' +const outputDir = 'public/images/figma/' + +const isFigmaLink = match => { + return match.startsWith('https://www.figma.com/design/') +} + +const findMatches = async (regexPattern, files) => { + const matches = await Promise.all( + files.map(async filePath => { + // read file content + const content = await fs.readFile(filePath, {encoding: 'utf8'}) + // try to find all matches in the file content + const matches = [...content.matchAll(regexPattern)] + // for each match, return the first group + return matches.map(match => match[1]).filter(isFigmaLink) + }), + ) + // + return matches.flat() +} + +const run = async () => { + // check for Figma API token before proceeding + if (!process.env.FIGMA_API_TOKEN) { + console.warn('⚠️ No Figma API token provided. Skipping image download from Figma.') + console.log('To get a Figma API token, visit https://www.figma.com/developers/api#access-tokens') + console.log('Then, add the token to an .env file in the root of the project with: FIGMA_API_TOKEN=your-token-here') + return + } + + // get all files that match the file glob + const files = await fastGlob([fileGlob]) + + // define the regex pattern to search + const pattern = /]*src="([^"]+)"[^>]*>/g + + // find matches in find + const nodeURLs = await findMatches(pattern, files) + + // fetch and download images + await figmaImages(process.env.FIGMA_API_TOKEN, { + nodeURLs, + outputDir, + missingImagesLogLevel: 'warn', + }) +} + +run() diff --git a/scripts/getFigmaImages.js b/scripts/getFigmaImages.js deleted file mode 100644 index dda344eac..000000000 --- a/scripts/getFigmaImages.js +++ /dev/null @@ -1,42 +0,0 @@ -// Import necessary modules -const fs = require('fs').promises; -const fastGlob = require('fast-glob'); - -const isFigmaLink = (match) => { - return match.startsWith('https://www.figma.com/design/') -} - -const findMatches = async (regexPattern, files) => { - const matches = await Promise.all( - files.map(async (filePath) => { - // read file content - const content = await fs.readFile(filePath, { encoding: 'utf8' }) - // try to find all matches in the file content - const matches = [...content.matchAll(regexPattern)] - // for each match, return the first group - return matches.map((match) => match[1]).filter(isFigmaLink); - }) - ) - // - return matches.flat() -} - -const run = async () => { - // get arguments - const [fileGlob] = process.argv.slice(2) - if(!fileGlob) { - console.error('❌ Please provide a file glob as the argument. It needs to be wrapped in quotes.') - return - } - // get all files that match the file glob - const files = await fastGlob([fileGlob]) - // define the regex pattern to search - const pattern = /]*src="([^"]+)"[^>]*>/g - // find matches in find - const matches = await findMatches(pattern, files) - // output result - console.log(JSON.stringify(matches, null, 2)) - -} - -run() \ No newline at end of file diff --git a/src/components/figma-image.tsx b/src/components/figma-image.tsx new file mode 100644 index 000000000..4daf9516a --- /dev/null +++ b/src/components/figma-image.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import styled from 'styled-components' +import {parseFigmaNodeUrl} from '@primer/figma-images/src/utils' +import {LinkButton, StyledOcticon} from '@primer/react' +import {PencilIcon} from '@primer/octicons-react' + +type FigmaImageProps = React.ImgHTMLAttributes & { + src: string + caption?: string +} + +const StyledImg = styled.img` + max-width: 100%; + height: auto; +` + +const StyledLinkButton = styled(LinkButton)` + position: absolute; + top: 16px; + right: 16px; + opacity: 0; +` + +const StyledFigure = styled.figure` + position: relative; + width: 100%; + margin: 0; + + &:hover ${StyledLinkButton} { + opacity: 1; + } +` + +const StyledCaption = styled.figcaption`` + +const FigmaImageDir = '/images/figma' +export const FigmaImage: React.FC = ({src, caption, ...props}) => { + // check for missing prop + if (src === undefined) throw new Error('src is required on FigmaImage component') + // get real image url + const {nodeId, fileId} = parseFigmaNodeUrl(src) + const imagePath = `${FigmaImageDir}/${fileId}-${nodeId}.png` + // return image component + return ( + + + + + Edit in Figma + + {caption && {caption}} + + ) +} diff --git a/yarn.lock b/yarn.lock index a3b505125..e6d870d31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1992,10 +1992,10 @@ resolved "https://registry.npmjs.org/@primer/component-metadata/-/component-metadata-0.5.1.tgz" integrity sha512-+3tuJScHWRifOAyMV+cn1I533j+PcprvPXbKnH1W7N+vhaGyaaHTao8Dkyyhxbhklmumcf8XR+Lz6dK1ojDrCg== -"@primer/figma-images@^0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@primer/figma-images/-/figma-images-0.1.2.tgz#e8fe6df96e23eaa9c1f69b6eb0f674e1be82173d" - integrity sha512-dfWVbs67GePlYyyKoXi0AiKdTxKP3ko5nkV7+mjiOVa9Hkba6ry1KUnTVnwhs8Xceube/BoXxU/LgfdflZkxag== +"@primer/figma-images@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@primer/figma-images/-/figma-images-0.1.5.tgz#0f073ae968b6d725cd3c115bee2143a75bb5d6d7" + integrity sha512-GsKIJ+pVKdui30fiXSj/sQkZLS5WkEbwYFQcJIehzU2dQ+r5DeL1IP+gwn0x8hvi5B9PELvdXMzsrGtb2at+rg== dependencies: axios "^1.3.4" dotenv "^16.0.3" @@ -5766,7 +5766,7 @@ dot-prop@^5.2.0: dependencies: is-obj "^2.0.0" -dotenv@^16.0.3: +dotenv@^16.0.3, dotenv@^16.4.5: version "16.4.5" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==