diff --git a/.github/scripts/storybook-preview-comment.cjs b/.github/scripts/storybook-preview-comment.cjs new file mode 100644 index 00000000000..ba5c7113c8f --- /dev/null +++ b/.github/scripts/storybook-preview-comment.cjs @@ -0,0 +1,136 @@ +// Creates or updates a single PR comment that links each changed component to +// its rendered story in the deployed Storybook previews. +// +// Invoked from .github/workflows/storybook.yml via actions/github-script: +// +// const run = require('./.github/scripts/storybook-preview-comment.cjs') +// await run({github, context}) +// +// The deploy results/URLs are passed in through the environment (CP_RESULT, +// CP_URL, RESOURCES_RESULT, RESOURCES_URL) so untrusted values never get +// interpolated into the workflow's inline script. + +const MARKER = ''; + +// Directory portion of a story's importPath, normalized for comparison against +// the repo-relative paths returned by the GitHub API. +const dirOf = (p) => + p + .replace(/^\.\//, '') + .split('/') + .slice(0, -1) + .join('/'); + +// Storybook publishes an index of every story at `/index.json`. We use +// it to turn a changed file into a deep link to its rendered component. +async function storyIndex(url) { + try { + const res = await fetch(`${url}/index.json`); + if (!res.ok) return []; + return Object.values((await res.json()).entries || {}); + } catch { + return []; + } +} + +// Build the markdown section for one preview: an "open Storybook" link plus a +// list of the components touched by this PR, each deep-linked into the preview. +async function sectionFor(preview, changed) { + if (preview.result !== 'success' || !preview.url) { + return `**${preview.name}** — ⚠️ deploy ${preview.result}, no preview available`; + } + + const entries = await storyIndex(preview.url); + + // A component "changed" if any changed file lives in the same directory as one + // of this Storybook's story entries. Collapse the several entries per story + // file down to one link per component, preferring the Docs page when present. + // + // Scope the changed-file matching to the relevant subtree so similarly-named + // directories elsewhere in the repo don't create false positives. + const baseDir = + preview.name === '@craftcms/cp' + ? 'packages/craftcms-cp' + : preview.name === 'resources/js' + ? 'resources/js' + : null; + + const changedInScope = baseDir + ? changed + .filter((file) => file.startsWith(`${baseDir}/`)) + .map((file) => file.slice(baseDir.length + 1)) + : changed; + + const byImport = new Map(); + for (const entry of entries) { + if (!entry?.importPath) continue; + const dir = dirOf(entry.importPath); + if (!dir || !changedInScope.some((file) => file.startsWith(`${dir}/`))) continue; + const current = byImport.get(entry.importPath); + if (!current || (entry.type === 'docs' && current.type !== 'docs')) { + byImport.set(entry.importPath, entry); + } + } + + const links = [...byImport.values()] + .sort((a, b) => a.title.localeCompare(b.title)) + .map((entry) => { + const kind = entry.type === 'docs' ? 'docs' : 'story'; + return `- [${entry.title}](${preview.url}/?path=/${kind}/${entry.id})`; + }); + + const heading = `**${preview.name}** — [open Storybook](${preview.url})`; + return links.length + ? `${heading}\n\nChanged components:\n${links.join('\n')}` + : `${heading}\n\n_No changed components detected in this Storybook._`; +} + +module.exports = async ({github, context}) => { + const previews = [ + {name: '@craftcms/cp', result: process.env.CP_RESULT, url: process.env.CP_URL}, + {name: 'resources/js', result: process.env.RESOURCES_RESULT, url: process.env.RESOURCES_URL}, + ]; + + // Every file changed in this PR (excluding deletions, which no longer have a + // rendered story to point at). + const changed = ( + await github.paginate(github.rest.pulls.listFiles, { + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.issue.number, + per_page: 100, + }) + ) + .filter((file) => file.status !== 'removed') + .map((file) => file.filename); + + const sections = []; + for (const preview of previews) { + sections.push(await sectionFor(preview, changed)); + } + + const body = `${MARKER}\n### 📚 Storybook previews\n\n${sections.join('\n\n')}`; + + const {data: comments} = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }); + const existing = comments.find((comment) => comment.body.includes(MARKER)); + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }); + } +}; diff --git a/.github/workflows/js-ci.yml b/.github/workflows/js-ci.yml index 80bb48918cc..8210e6d77cd 100644 --- a/.github/workflows/js-ci.yml +++ b/.github/workflows/js-ci.yml @@ -15,6 +15,9 @@ env: NODE_VERSION: '22' PHP_VERSION: '8.5' +permissions: + contents: read + jobs: setup: name: 'Setup / Install + Build + Generate' diff --git a/.github/workflows/laravel-ci.yml b/.github/workflows/laravel-ci.yml index b79908c5471..9a913005e37 100644 --- a/.github/workflows/laravel-ci.yml +++ b/.github/workflows/laravel-ci.yml @@ -11,6 +11,9 @@ concurrency: group: laravel-ci-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: pint: name: 'Code Quality / Pint' @@ -101,7 +104,7 @@ jobs: - name: Install composer dependencies uses: ramsey/composer-install@v3 - + - name: PHPStan Cache uses: actions/cache@v4 with: diff --git a/.github/workflows/monorepo-split.yml b/.github/workflows/monorepo-split.yml index d201bb53a02..88bb8e07847 100644 --- a/.github/workflows/monorepo-split.yml +++ b/.github/workflows/monorepo-split.yml @@ -9,6 +9,9 @@ on: - feature/split-packages tags: '*' +permissions: + contents: read + jobs: split-monorepo: runs-on: ubuntu-latest diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml new file mode 100644 index 00000000000..6271cc667ea --- /dev/null +++ b/.github/workflows/storybook.yml @@ -0,0 +1,234 @@ +name: 'Storybook' +on: + workflow_dispatch: + push: + branches: + - 6.x + paths: + - 'packages/craftcms-cp/**' + - 'packages/craftcms-garnish/**' + - 'resources/js/**' + - 'src/**/*.php' + pull_request: + paths: + - 'packages/craftcms-cp/**' + - 'packages/craftcms-garnish/**' + - 'resources/js/**' + - 'src/**/*.php' + +concurrency: + group: storybook-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + NODE_VERSION: '22' + PHP_VERSION: '8.5' + +permissions: + contents: read + +jobs: + # Gate: install deps, build the packages, and generate the TS/wayfinder + # output. The deploy jobs only run once this succeeds, so we never publish a + # Storybook built from assets that don't compile. Results are cached and + # restored by the parallel deploy jobs below. + build-assets: + name: 'Build assets' + runs-on: ubuntu-latest + timeout-minutes: 15 + outputs: + cache-key: ${{ steps.cache-key.outputs.value }} + deploy-branch: ${{ steps.branch.outputs.value }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.PHP_VERSION }} + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Compute cache key + id: cache-key + # Keep this in sync with .github/workflows/js-ci.yml so the built assets + # and generated files are shared between the two workflows. + run: | + echo "value=js-ci-v3-${{ runner.os }}-${{ hashFiles('package-lock.json', 'composer.lock', 'package.json', 'packages/*/package.json', 'packages/craftcms-cp/src/**', 'packages/craftcms-cp/tsconfig*.json', 'packages/craftcms-cp/vite.config.*', 'packages/craftcms-cp/tsdown.config.*', 'packages/craftcms-garnish/src/**', 'packages/craftcms-garnish/tsconfig*.json', 'packages/craftcms-garnish/tsdown.config.*', 'src/**/*.php', 'workbench/**/*.php') }}" >> "$GITHUB_OUTPUT" + + - name: Restore install + build + generated cache + id: cache + uses: actions/cache@v4 + with: + path: | + node_modules + packages/*/node_modules + packages/craftcms-cp/dist + packages/craftcms-garnish/dist + vendor + resources/js/actions + resources/js/routes + resources/js/wayfinder + resources/js/generated + key: ${{ steps.cache-key.outputs.value }} + + - name: Install Composer dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: composer install --no-interaction --no-progress + + - name: Install npm dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: npm ci + env: + CRAFT_FONTAWESOME_TOKEN: ${{ secrets.CRAFT_FONTAWESOME_TOKEN }} + + # Build the garnish package so its `dist/*.d.ts` exist — `vue-tsc` resolves + # `@craftcms/garnish` from `dist` (its `types`), and dist is gitignored. + - name: Build @craftcms/garnish + if: steps.cache.outputs.cache-hit != 'true' + run: npm run build:garnish + + - name: Build @craftcms/cp + if: steps.cache.outputs.cache-hit != 'true' + run: npm run build:cp + + - name: Generate TypeScript types + if: steps.cache.outputs.cache-hit != 'true' + run: npm run generate:types + + - name: Generate Wayfinder routes + if: steps.cache.outputs.cache-hit != 'true' + run: npm run generate:wayfinder + + - name: Determine deploy branch + id: branch + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "value=pr-${{ github.event.number }}" >> "$GITHUB_OUTPUT" + else + echo "value=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" + fi + + deploy-cp: + name: 'Deploy / craftcms-cp' + runs-on: ubuntu-latest + timeout-minutes: 10 + needs: build-assets + outputs: + url: ${{ steps.deploy.outputs.deployment-url }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Restore install + build + generated cache + uses: actions/cache@v4 + with: + path: | + node_modules + packages/*/node_modules + packages/craftcms-cp/dist + packages/craftcms-garnish/dist + vendor + resources/js/actions + resources/js/routes + resources/js/wayfinder + resources/js/generated + key: ${{ needs.build-assets.outputs.cache-key }} + + - name: Build Storybook + run: npm run build:storybook --workspace=@craftcms/cp + + - name: Deploy to Cloudflare Pages + id: deploy + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy packages/craftcms-cp/storybook-static --project-name=craftcms-ui-storybook --branch=${{ needs.build-assets.outputs.deploy-branch }} + + deploy-resources: + name: 'Deploy / resources' + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: build-assets + outputs: + url: ${{ steps.deploy.outputs.deployment-url }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Restore install + build + generated cache + uses: actions/cache@v4 + with: + path: | + node_modules + packages/*/node_modules + packages/craftcms-cp/dist + packages/craftcms-garnish/dist + vendor + resources/js/actions + resources/js/routes + resources/js/wayfinder + resources/js/generated + key: ${{ needs.build-assets.outputs.cache-key }} + + - name: Build Storybook + run: npm run build:storybook + + - name: Deploy to Cloudflare Pages + id: deploy + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy storybook-static --project-name=craftcms-storybook --branch=${{ needs.build-assets.outputs.deploy-branch }} + + # Single writer for the PR comment. Runs after both deploys (regardless of + # their result) and renders one comment covering both previews, so the two + # parallel jobs never race to create duplicate comments. Skipped entirely if + # the gate failed and neither deploy ran. + comment: + name: 'Comment preview URLs' + runs-on: ubuntu-latest + needs: [deploy-cp, deploy-resources] + if: ${{ always() && github.event_name == 'pull_request' && (needs.deploy-cp.result != 'skipped' || needs.deploy-resources.result != 'skipped') }} + permissions: + contents: read + pull-requests: write + steps: + # Checkout so the comment script is on disk for github-script to require. + # Only the scripts directory is needed, so keep the checkout sparse. + - name: Checkout comment script + uses: actions/checkout@v4 + with: + sparse-checkout: .github/scripts + sparse-checkout-cone-mode: false + + - name: Create or update the preview comment + uses: actions/github-script@v7 + env: + CP_RESULT: ${{ needs.deploy-cp.result }} + CP_URL: ${{ needs.deploy-cp.outputs.url }} + RESOURCES_RESULT: ${{ needs.deploy-resources.result }} + RESOURCES_URL: ${{ needs.deploy-resources.outputs.url }} + with: + script: | + const run = require('./.github/scripts/storybook-preview-comment.cjs') + await run({github, context}) diff --git a/package-lock.json b/package-lock.json index ec6d11263d8..aa3c866b4f3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2208,9 +2208,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", - "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" ], @@ -2224,9 +2224,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", - "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" ], @@ -2240,9 +2240,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", - "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" ], @@ -2256,9 +2256,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", - "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], @@ -2272,7 +2272,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.0", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" ], @@ -2286,9 +2288,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", - "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" ], @@ -2302,9 +2304,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", - "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], @@ -2318,9 +2320,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", - "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" ], @@ -2334,9 +2336,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", - "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" ], @@ -2350,9 +2352,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", - "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" ], @@ -2366,9 +2368,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", - "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" ], @@ -2382,9 +2384,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", - "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" ], @@ -2398,9 +2400,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", - "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" ], @@ -2414,9 +2416,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", - "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" ], @@ -2430,9 +2432,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", - "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" ], @@ -2446,9 +2448,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", - "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" ], @@ -2462,9 +2464,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", - "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -2478,9 +2480,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", - "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" ], @@ -2494,9 +2496,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", - "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" ], @@ -2510,9 +2512,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", - "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" ], @@ -2526,9 +2528,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", - "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" ], @@ -2542,9 +2544,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", - "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" ], @@ -2558,9 +2560,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", - "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], @@ -2574,9 +2576,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", - "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" ], @@ -2590,9 +2592,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", - "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" ], @@ -2606,9 +2608,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", - "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" ], @@ -11816,7 +11818,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.28.0", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -11826,32 +11830,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.0", - "@esbuild/android-arm": "0.28.0", - "@esbuild/android-arm64": "0.28.0", - "@esbuild/android-x64": "0.28.0", - "@esbuild/darwin-arm64": "0.28.0", - "@esbuild/darwin-x64": "0.28.0", - "@esbuild/freebsd-arm64": "0.28.0", - "@esbuild/freebsd-x64": "0.28.0", - "@esbuild/linux-arm": "0.28.0", - "@esbuild/linux-arm64": "0.28.0", - "@esbuild/linux-ia32": "0.28.0", - "@esbuild/linux-loong64": "0.28.0", - "@esbuild/linux-mips64el": "0.28.0", - "@esbuild/linux-ppc64": "0.28.0", - "@esbuild/linux-riscv64": "0.28.0", - "@esbuild/linux-s390x": "0.28.0", - "@esbuild/linux-x64": "0.28.0", - "@esbuild/netbsd-arm64": "0.28.0", - "@esbuild/netbsd-x64": "0.28.0", - "@esbuild/openbsd-arm64": "0.28.0", - "@esbuild/openbsd-x64": "0.28.0", - "@esbuild/openharmony-arm64": "0.28.0", - "@esbuild/sunos-x64": "0.28.0", - "@esbuild/win32-arm64": "0.28.0", - "@esbuild/win32-ia32": "0.28.0", - "@esbuild/win32-x64": "0.28.0" + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/escalade": { @@ -23731,7 +23735,9 @@ } }, "node_modules/ws": { - "version": "8.20.0", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 0f0d4758ddc..98f4e0331f1 100644 --- a/package.json +++ b/package.json @@ -40,9 +40,9 @@ "node": ">=22" }, "devDependencies": { + "@craftcms/garnish": "file:packages/craftcms-garnish", "@craftcms/playwright": "file:packages/craftcms-playwright", "@craftcms/webpack": "file:packages/craftcms-webpack", - "@craftcms/garnish": "file:packages/craftcms-garnish", "@laravel/vite-plugin-wayfinder": "^0.1.7", "@playwright/test": "^1.59.1", "@storybook/addon-a11y": "^10.4.1", diff --git a/packages/craftcms-cp/package.json b/packages/craftcms-cp/package.json index 7e6eff9880b..bcf5abfccad 100644 --- a/packages/craftcms-cp/package.json +++ b/packages/craftcms-cp/package.json @@ -25,7 +25,9 @@ "check:format": "prettier --check ./src", "check:exports": "attw --pack .", "check:types": "tsc --noEmit", + "prestorybook": "npm run build:manifest", "storybook": "storybook dev -p 6006", + "prebuild:storybook": "npm run build:manifest", "build:storybook": "storybook build", "build:manifest": "custom-elements-manifest analyze --litelement --outdir dist", "generate:vue-wrappers": "node ./scripts/generate-vue-wrappers.js", diff --git a/packages/craftcms-cp/src/styles/shared/variables.css b/packages/craftcms-cp/src/styles/shared/variables.css index 863a86a36ad..f9ae07ab4ce 100644 --- a/packages/craftcms-cp/src/styles/shared/variables.css +++ b/packages/craftcms-cp/src/styles/shared/variables.css @@ -143,33 +143,33 @@ --c-color-on-quiet: var(--c-color-neutral-on-quiet); --c-color-on-normal: var(--c-color-neutral-on-normal); --c-color-on-loud: var(--c-color-neutral-on-loud); +} - @keyframes spin { - to { - transform: rotate(360deg); - } +@keyframes spin { + to { + transform: rotate(360deg); } - @keyframes ping { - 75%, - 100% { - transform: scale(2); - opacity: 0; - } +} +@keyframes ping { + 75%, + 100% { + transform: scale(2); + opacity: 0; } - @keyframes pulse { - 50% { - opacity: 0.5; - } +} +@keyframes pulse { + 50% { + opacity: 0.5; + } +} +@keyframes bounce { + 0%, + 100% { + transform: translateY(-25%); + animation-timing-function: cubic-bezier(0.8, 0, 1, 1); } - @keyframes bounce { - 0%, - 100% { - transform: translateY(-25%); - animation-timing-function: cubic-bezier(0.8, 0, 1, 1); - } - 50% { - transform: none; - animation-timing-function: cubic-bezier(0, 0, 0.2, 1); - } + 50% { + transform: none; + animation-timing-function: cubic-bezier(0, 0, 0.2, 1); } }