diff --git a/.changeset/spotty-coats-post.md b/.changeset/spotty-coats-post.md new file mode 100644 index 00000000000..da5a61bbb5f --- /dev/null +++ b/.changeset/spotty-coats-post.md @@ -0,0 +1,5 @@ +--- +'@primer/styled-react': minor +--- + +Create the `@primer/styled-react` package to help with bridging between styled-components and Primer React diff --git a/package-lock.json b/package-lock.json index 185e46edb32..675d11256b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5943,6 +5943,10 @@ "resolved": "packages/react", "link": true }, + "node_modules/@primer/styled-react": { + "resolved": "packages/styled-react", + "link": true + }, "node_modules/@primer/stylelint-config": { "version": "13.3.0", "resolved": "https://registry.npmjs.org/@primer/stylelint-config/-/stylelint-config-13.3.0.tgz", @@ -7631,7 +7635,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.3.0", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", "dev": true, "license": "MIT", "dependencies": { @@ -26895,16 +26901,6 @@ "integrity": "sha512-iFrvar5SOMtKFOSjYvs4z9UlLqDdJbMx0mgISLcPedv+g0ac5sgeETLGtipHCVIae6HJPclNEH5aCyD1RZaEHw==", "license": "BSD-3-Clause" }, - "packages/react/node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, "packages/react/node_modules/@types/react-test-renderer": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.1.tgz", @@ -27355,6 +27351,182 @@ "funding": { "url": "https://github.com/sponsors/isaacs" } + }, + "packages/styled-react": { + "name": "@primer/styled-react", + "version": "0.0.0", + "devDependencies": { + "@babel/preset-typescript": "^7.27.1", + "@primer/react": "^37.29.1", + "@rollup/plugin-babel": "^6.0.4", + "@types/react": "18.3.11", + "@types/react-dom": "18.3.1", + "react": "18.3.1", + "react-dom": "18.3.1", + "rimraf": "^6.0.1", + "rollup": "4.45.1", + "rollup-plugin-typescript2": "^0.36.0", + "styled-components": "5.3.11", + "typescript": "^5.8.2" + }, + "peerDependencies": { + "@primer/react": "^37.29.1", + "@types/react": "18.x || 19.x", + "@types/react-dom": "18.x || 19.x", + "@types/react-is": "18.x || 19.x", + "@types/styled-components": "^5.1.11", + "react": "18.x || 19.x", + "react-dom": "18.x || 19.x", + "react-is": "18.x || 19.x", + "styled-components": "5.x" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "@types/react-is": { + "optional": true + }, + "@types/styled-components": { + "optional": true + } + } + }, + "packages/styled-react/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/styled-react/node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/styled-react/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/styled-react/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "packages/styled-react/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/styled-react/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/styled-react/node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/styled-react/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } } } } diff --git a/packages/styled-react/README.md b/packages/styled-react/README.md new file mode 100644 index 00000000000..c69be89bf01 --- /dev/null +++ b/packages/styled-react/README.md @@ -0,0 +1,33 @@ +# @primer/styled-react + +> A temporary package that bridges the gap between Primer React and styled-components + +## Getting started + +To install `@primer/styled-react` in your project, you will need to run the following +command using [npm](https://www.npmjs.com/): + +```bash +npm install -S @primer/styled-react +``` + +## Usage + +This is a pre-1.0 package designed to interop between Primer and existing +styled-components usage. As a result, expect breaking changes between minor +versions as components will be removed when their `sx` usage goes to zero. + +Each component that is imported from `@primer/styled-react` is a wrapper around +the corresponding component in `@primer/react`. The notable difference is that +these components support the `sx` prop and styled system props. + +By default, each component is deprecated. The intent is to transition code over +to an alternative styling solution, such as CSS Modules. + +## 📖 Documentation + +The documentation for `@primer/react` lives at [primer.style](https://primer.style). There, you'll find detailed documentation on getting started, all of the components, our theme, our principles, and more. + +## 🙌 Contributing + +We love collaborating with folks inside and outside of GitHub and welcome contributions! If you're interested, check out our [contributing docs](contributor-docs/CONTRIBUTING.md) for more info on how to get started. diff --git a/packages/styled-react/package.json b/packages/styled-react/package.json new file mode 100644 index 00000000000..fa466c40e0f --- /dev/null +++ b/packages/styled-react/package.json @@ -0,0 +1,67 @@ +{ + "name": "@primer/styled-react", + "version": "0.0.0", + "type": "module", + "exports": { + ".": { + "types": "dist/index.d.ts", + "default": "dist/index.js" + }, + "./deprecated": { + "types": "dist/deprecated.d.ts", + "default": "dist/deprecated.js" + }, + "./experimental": { + "types": "dist/experimental.d.ts", + "default": "dist/experimental.js" + } + }, + "files": [ + "README.md", + "dist" + ], + "scripts": { + "build": "script/build", + "clean": "rimraf dist", + "type-check": "tsc --noEmit" + }, + "devDependencies": { + "@babel/preset-typescript": "^7.27.1", + "@primer/react": "^37.29.1", + "@rollup/plugin-babel": "^6.0.4", + "@types/react": "18.3.11", + "@types/react-dom": "18.3.1", + "react": "18.3.1", + "react-dom": "18.3.1", + "rimraf": "^6.0.1", + "rollup": "4.45.1", + "rollup-plugin-typescript2": "^0.36.0", + "styled-components": "5.3.11", + "typescript": "^5.8.2" + }, + "peerDependencies": { + "@primer/react": "^37.29.1", + "@types/react": "18.x || 19.x", + "@types/react-dom": "18.x || 19.x", + "@types/react-is": "18.x || 19.x", + "@types/styled-components": "^5.1.11", + "react": "18.x || 19.x", + "react-dom": "18.x || 19.x", + "react-is": "18.x || 19.x", + "styled-components": "5.x" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "@types/react-is": { + "optional": true + }, + "@types/styled-components": { + "optional": true + } + } +} diff --git a/packages/styled-react/rollup.config.js b/packages/styled-react/rollup.config.js new file mode 100644 index 00000000000..0c5deb298b0 --- /dev/null +++ b/packages/styled-react/rollup.config.js @@ -0,0 +1,33 @@ +import babel from '@rollup/plugin-babel' +import {defineConfig} from 'rollup' +import typescript from 'rollup-plugin-typescript2' +import packageJson from './package.json' with {type: 'json'} + +const dependencies = [ + ...Object.keys(packageJson.peerDependencies ?? {}), + ...Object.keys(packageJson.dependencies ?? {}), + ...Object.keys(packageJson.devDependencies ?? {}), +] + +function createPackageRegex(name) { + return new RegExp(`^${name}(/.*)?`) +} + +export default defineConfig({ + input: ['src/index.ts', 'src/experimental.ts', 'src/deprecated.ts'], + external: dependencies.map(createPackageRegex), + plugins: [ + typescript({ + tsconfig: 'tsconfig.build.json', + }), + babel({ + presets: ['@babel/preset-typescript'], + extensions: ['.ts', '.tsx'], + babelHelpers: 'bundled', + }), + ], + output: { + dir: 'dist', + format: 'esm', + }, +}) diff --git a/packages/styled-react/script/build b/packages/styled-react/script/build new file mode 100755 index 00000000000..b2981e2b226 --- /dev/null +++ b/packages/styled-react/script/build @@ -0,0 +1,3 @@ +#!/bin/bash + +npx rollup -c diff --git a/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap new file mode 100644 index 00000000000..fd2e5a7925a --- /dev/null +++ b/packages/styled-react/src/__tests__/__snapshots__/exports.test.ts.snap @@ -0,0 +1,35 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`@primer/styled-react exports 1`] = ` +[ + "ActionList", + "ActionMenu", + "Box", + "Breadcrumbs", + "Button", + "Flash", + "FormControl", + "Heading", + "IconButton", + "Label", + "Link", + "LinkButton", + "PageLayout", + "Text", + "TextInput", + "Truncate", + "sx", +] +`; + +exports[`@primer/styled-react/deprecated exports 1`] = ` +[ + "Octicon", +] +`; + +exports[`@primer/styled-react/experimental exports 1`] = ` +[ + "Dialog", +] +`; diff --git a/packages/styled-react/src/__tests__/exports.test.ts b/packages/styled-react/src/__tests__/exports.test.ts new file mode 100644 index 00000000000..1ef4ed8b05d --- /dev/null +++ b/packages/styled-react/src/__tests__/exports.test.ts @@ -0,0 +1,19 @@ +import {test, expect} from 'vitest' +// eslint-disable-next-line import/no-namespace +import * as StyledReact from '../' +// eslint-disable-next-line import/no-namespace +import * as StyledReactDeprecated from '../deprecated' +// eslint-disable-next-line import/no-namespace +import * as StyledReactExperimental from '../experimental' + +test('@primer/styled-react exports', () => { + expect(Object.keys(StyledReact)).toMatchSnapshot() +}) + +test('@primer/styled-react/deprecated exports', () => { + expect(Object.keys(StyledReactDeprecated)).toMatchSnapshot() +}) + +test('@primer/styled-react/experimental exports', () => { + expect(Object.keys(StyledReactExperimental)).toMatchSnapshot() +}) diff --git a/packages/styled-react/src/deprecated.ts b/packages/styled-react/src/deprecated.ts new file mode 100644 index 00000000000..59f36ac26ef --- /dev/null +++ b/packages/styled-react/src/deprecated.ts @@ -0,0 +1 @@ +export {Octicon} from '@primer/react/deprecated' diff --git a/packages/styled-react/src/experimental.ts b/packages/styled-react/src/experimental.ts new file mode 100644 index 00000000000..75a2979faf2 --- /dev/null +++ b/packages/styled-react/src/experimental.ts @@ -0,0 +1 @@ +export {Dialog} from '@primer/react/experimental' diff --git a/packages/styled-react/src/index.ts b/packages/styled-react/src/index.ts new file mode 100644 index 00000000000..c41a1a9bded --- /dev/null +++ b/packages/styled-react/src/index.ts @@ -0,0 +1,24 @@ +export { + ActionList, + ActionMenu, + Box, + type BoxProps, + Breadcrumbs, + Button, + Flash, + FormControl, + Heading, + IconButton, + Label, + Link, + LinkButton, + PageLayout, + Text, + TextInput, + Truncate, + + // Utilities for working with the `sx` prop + sx, + type SxProp, + type BetterSystemStyleObject, +} from '@primer/react' diff --git a/packages/styled-react/tsconfig.build.json b/packages/styled-react/tsconfig.build.json new file mode 100644 index 00000000000..32f0e2e8057 --- /dev/null +++ b/packages/styled-react/tsconfig.build.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "emitDeclarationOnly": true + }, + "exclude": ["vitest.config.ts"] +} diff --git a/packages/styled-react/tsconfig.json b/packages/styled-react/tsconfig.json new file mode 100644 index 00000000000..9e42ce6a611 --- /dev/null +++ b/packages/styled-react/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src", "vitest.config.ts"] +} + diff --git a/packages/styled-react/vitest.config.ts b/packages/styled-react/vitest.config.ts new file mode 100644 index 00000000000..e28d08c4e78 --- /dev/null +++ b/packages/styled-react/vitest.config.ts @@ -0,0 +1,7 @@ +import {defineConfig} from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'node', + }, +})