diff --git a/.github/scripts/dev.js b/.github/scripts/dev.js index 2a3b0b4ade..257282701b 100644 --- a/.github/scripts/dev.js +++ b/.github/scripts/dev.js @@ -74,7 +74,7 @@ const COMMAND_TYPESCRIPT = { env: {} }; -const adminXApps = '@tryghost/admin-x-demo,@tryghost/admin-x-settings,@tryghost/admin-x-activitypub'; +const adminXApps = '@tryghost/admin-x-demo,@tryghost/admin-x-settings,@tryghost/admin-x-activitypub,@tryghost/posts'; const COMMANDS_ADMINX = [{ name: 'adminXDeps', diff --git a/apps/admin-x-framework/package.json b/apps/admin-x-framework/package.json index 836ef70722..2c22e5288d 100644 --- a/apps/admin-x-framework/package.json +++ b/apps/admin-x-framework/package.json @@ -1,119 +1,119 @@ { - "name": "@tryghost/admin-x-framework", - "type": "module", - "version": "0.0.0", - "repository": "https://github.com/TryGhost/Ghost/tree/main/apps/admin-x-framework", - "author": "Ghost Foundation", - "private": true, - "exports": { - ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs", - "types": "./types/index.d.ts" + "name": "@tryghost/admin-x-framework", + "type": "module", + "version": "0.0.0", + "repository": "https://github.com/TryGhost/Ghost/tree/main/apps/admin-x-framework", + "author": "Ghost Foundation", + "private": true, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.cjs", + "types": "./types/index.d.ts" + }, + "./errors": { + "import": "./dist/errors.js", + "require": "./dist/errors.cjs", + "types": "./types/errors.d.ts" + }, + "./helpers": { + "import": "./dist/helpers.js", + "require": "./dist/helpers.cjs", + "types": "./types/helpers.d.ts" + }, + "./hooks": { + "import": "./dist/hooks.js", + "require": "./dist/hooks.cjs", + "types": "./types/hooks.d.ts" + }, + "./routing": { + "import": "./dist/routing.js", + "require": "./dist/routing.cjs", + "types": "./types/routing.d.ts" + }, + "./api/*": { + "import": "./dist/api/*.js", + "require": "./dist/api/*.cjs", + "types": "./types/api/*.d.ts" + }, + "./vite": { + "import": "./dist/vite.js", + "require": "./dist/vite.cjs", + "types": "./types/vite.d.ts" + }, + "./playwright": { + "import": "./dist/playwright.js", + "require": "./dist/playwright.cjs", + "types": "./types/playwright.d.ts" + }, + "./test/*": { + "import": "./dist/test/*.js", + "require": "./dist/test/*.cjs", + "types": "./types/test/*.d.ts" + } }, - "./errors": { - "import": "./dist/errors.js", - "require": "./dist/errors.cjs", - "types": "./types/errors.d.ts" + "sideEffects": false, + "scripts": { + "build": "tsc -p tsconfig.declaration.json && vite build", + "prepare": "yarn build", + "test": "yarn test:types && yarn test:unit", + "test:types": "tsc --noEmit", + "test:unit": "vitest run --coverage", + "lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache", + "lint": "yarn lint:code && yarn lint:test", + "lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx test/ --cache" }, - "./helpers": { - "import": "./dist/helpers.js", - "require": "./dist/helpers.cjs", - "types": "./types/helpers.d.ts" + "files": [ + "es", + "types" + ], + "devDependencies": { + "@testing-library/react": "14.3.1", + "@types/mocha": "10.0.1", + "c8": "8.0.1", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-refresh": "0.4.3", + "jsdom": "24.1.3", + "mocha": "10.2.0", + "react": "18.3.1", + "react-dom": "18.3.1", + "sinon": "17.0.0", + "ts-node": "10.9.2", + "typescript": "5.4.5" }, - "./hooks": { - "import": "./dist/hooks.js", - "require": "./dist/hooks.cjs", - "types": "./types/hooks.d.ts" + "dependencies": { + "@sentry/react": "7.119.2", + "@tanstack/react-query": "4.36.1", + "@tryghost/admin-x-design-system": "0.0.0", + "@tryghost/shade": "0.0.0", + "@types/react": "18.3.3", + "@types/react-dom": "18.3.0", + "@vitejs/plugin-react": "4.2.1", + "react": "18.3.1", + "react-dom": "18.3.1", + "vite": "4.5.3", + "vite-plugin-css-injected-by-js": "^3.3.0", + "vite-plugin-svgr": "3.3.0", + "vitest": "0.34.3" }, - "./routing": { - "import": "./dist/routing.js", - "require": "./dist/routing.cjs", - "types": "./types/routing.d.ts" + "peerDependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0" }, - "./api/*": { - "import": "./dist/api/*.js", - "require": "./dist/api/*.cjs", - "types": "./types/api/*.d.ts" - }, - "./vite": { - "import": "./dist/vite.js", - "require": "./dist/vite.cjs", - "types": "./types/vite.d.ts" - }, - "./playwright": { - "import": "./dist/playwright.js", - "require": "./dist/playwright.cjs", - "types": "./types/playwright.d.ts" - }, - "./test/*": { - "import": "./dist/test/*.js", - "require": "./dist/test/*.cjs", - "types": "./types/test/*.d.ts" + "nx": { + "targets": { + "dev": { + "dependsOn": [ + "^build" + ] + }, + "test:unit": { + "dependsOn": [ + "test:unit", + "^build", + "@tryghost/admin-x-design-system:test:unit" + ] + } + } } - }, - "sideEffects": false, - "scripts": { - "build": "tsc -p tsconfig.declaration.json && vite build", - "prepare": "yarn build", - "test": "yarn test:types && yarn test:unit", - "test:types": "tsc --noEmit", - "test:unit": "vitest run --coverage", - "lint:code": "eslint --ext .js,.ts,.cjs,.tsx src/ --cache", - "lint": "yarn lint:code && yarn lint:test", - "lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx test/ --cache" - }, - "files": [ - "es", - "types" - ], - "devDependencies": { - "@testing-library/react": "14.3.1", - "@types/mocha": "10.0.1", - "c8": "8.0.1", - "eslint-plugin-react-hooks": "4.6.0", - "eslint-plugin-react-refresh": "0.4.3", - "jsdom": "24.1.3", - "mocha": "10.2.0", - "react": "18.3.1", - "react-dom": "18.3.1", - "sinon": "17.0.0", - "ts-node": "10.9.2", - "typescript": "5.4.5" - }, - "dependencies": { - "@sentry/react": "7.119.2", - "@tanstack/react-query": "4.36.1", - "@tryghost/admin-x-design-system": "0.0.0", - "@tryghost/shade": "0.0.0", - "@types/react": "18.3.3", - "@types/react-dom": "18.3.0", - "@vitejs/plugin-react": "4.2.1", - "react": "18.3.1", - "react-dom": "18.3.1", - "vite": "4.5.3", - "vite-plugin-css-injected-by-js": "^3.3.0", - "vite-plugin-svgr": "3.3.0", - "vitest": "0.34.3" - }, - "peerDependencies": { - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "nx": { - "targets": { - "dev": { - "dependsOn": [ - "^build" - ] - }, - "test:unit": { - "dependsOn": [ - "test:unit", - "^build", - "@tryghost/admin-x-design-system:test:unit" - ] - } - } - } } \ No newline at end of file diff --git a/apps/admin-x-framework/src/test/render-shade.tsx b/apps/admin-x-framework/src/test/render-shade.tsx new file mode 100644 index 0000000000..55206000b0 --- /dev/null +++ b/apps/admin-x-framework/src/test/render-shade.tsx @@ -0,0 +1,62 @@ +import {ShadeAppProps} from '@tryghost/shade'; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import {TopLevelFrameworkProps} from '../providers/FrameworkProvider'; + +export default function renderShadeApp( + App: React.ComponentType, + props: Props +) { + const style = document.createElement('style'); + style.appendChild(document.createTextNode(` + :root { + font-size: 62.5%; + line-height: 1.5; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + } + + html, body, #root { + width: 100%; + height: 100%; + margin: 0; + letter-spacing: unset; + } + `)); + document.head.appendChild(style); + + ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + { + // Use the expectExternalNavigate helper to test this dummy external linking + window.location.href = `/external/${encodeURIComponent(JSON.stringify(link))}`; + }, + ghostVersion: '5.x', + sentryDSN: null, + unsplashConfig: { + Authorization: '', + 'Accept-Version': '', + 'Content-Type': '', + 'App-Pragma': '', + 'X-Unsplash-Cache': false + }, + onDelete: () => {}, + onInvalidate: () => {}, + onUpdate: () => {} + }} + {...props} + /> + + ); +} diff --git a/apps/admin-x-framework/vite.config.ts b/apps/admin-x-framework/vite.config.ts index ea71b260ad..6340e9ab27 100644 --- a/apps/admin-x-framework/vite.config.ts +++ b/apps/admin-x-framework/vite.config.ts @@ -1,3 +1,4 @@ +import path from 'path'; import react from '@vitejs/plugin-react'; import glob from 'glob'; import {resolve} from 'path'; @@ -10,6 +11,11 @@ export default (function viteConfig() { plugins: [ react() ], + resolve: { + alias: { + '@': path.resolve(__dirname, './src') + } + }, preview: { port: 4174 }, @@ -20,13 +26,13 @@ export default (function viteConfig() { outDir: 'dist', lib: { formats: ['es', 'cjs'], - entry: glob.sync(resolve(__dirname, 'src/**/*.{ts,tsx}')).reduce((entries, path) => { - if (path.endsWith('.d.ts')) { + entry: glob.sync(resolve(__dirname, 'src/**/*.{ts,tsx}')).reduce((entries, libpath) => { + if (libpath.endsWith('.d.ts')) { return entries; } - const outPath = path.replace(resolve(__dirname, 'src') + '/', '').replace(/\.(ts|tsx)$/, ''); - entries[outPath] = path; + const outPath = libpath.replace(resolve(__dirname, 'src') + '/', '').replace(/\.(ts|tsx)$/, ''); + entries[outPath] = libpath; return entries; }, {} as Record) }, diff --git a/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx b/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx index f0dab043a2..3d588db18f 100644 --- a/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx +++ b/apps/admin-x-settings/src/components/settings/advanced/labs/AlphaFeatures.tsx @@ -51,6 +51,10 @@ const features = [{ title: 'Comment Improvements', description: 'Enables new comment features', flag: 'commentImprovements' +}, { + title: 'Post analytics redesign', + description: 'Enables redesigned Post analytics page', + flag: 'postsX' }]; const AlphaFeatures: React.FC = () => { diff --git a/apps/posts/.eslintrc.cjs b/apps/posts/.eslintrc.cjs new file mode 100644 index 0000000000..919b0f2cdf --- /dev/null +++ b/apps/posts/.eslintrc.cjs @@ -0,0 +1,56 @@ +/* eslint-env node */ +module.exports = { + root: true, + extends: [ + 'plugin:ghost/ts', + 'plugin:react/recommended', + 'plugin:react-hooks/recommended' + ], + plugins: [ + 'ghost', + 'react-refresh', + 'tailwindcss' + ], + settings: { + react: { + version: 'detect' + } + }, + rules: { + // sort multiple import lines into alphabetical groups + 'ghost/sort-imports-es6-autofix/sort-imports-es6': ['error', { + memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple'] + }], + + // TODO: re-enable this (maybe fixed fast refresh?) + 'react-refresh/only-export-components': 'off', + + // suppress errors for missing 'import React' in JSX files, as we don't need it + 'react/react-in-jsx-scope': 'off', + // ignore prop-types for now + 'react/prop-types': 'off', + + // TODO: re-enable these if deemed useful + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-empty-function': 'off', + + // custom react rules + 'react/jsx-sort-props': ['error', { + reservedFirst: true, + callbacksLast: true, + shorthandLast: true, + locale: 'en' + }], + 'react/button-has-type': 'error', + 'react/no-array-index-key': 'error', + 'react/jsx-key': 'off', + + 'tailwindcss/classnames-order': ['error', {config: 'tailwind.config.cjs'}], + 'tailwindcss/enforces-negative-arbitrary-values': ['warn', {config: 'tailwind.config.cjs'}], + 'tailwindcss/enforces-shorthand': ['warn', {config: 'tailwind.config.cjs'}], + 'tailwindcss/migration-from-tailwind-2': ['warn', {config: 'tailwind.config.cjs'}], + 'tailwindcss/no-arbitrary-value': 'off', + 'tailwindcss/no-custom-classname': 'off', + 'tailwindcss/no-contradicting-classname': ['error', {config: 'tailwind.config.cjs'}] + } +}; diff --git a/apps/posts/.gitignore b/apps/posts/.gitignore new file mode 100644 index 0000000000..68565785a7 --- /dev/null +++ b/apps/posts/.gitignore @@ -0,0 +1,3 @@ +dist +playwright-report +test-results diff --git a/apps/posts/index.html b/apps/posts/index.html new file mode 100644 index 0000000000..26af8c7232 --- /dev/null +++ b/apps/posts/index.html @@ -0,0 +1,16 @@ + + + + + + + + Posts + + + +
+ + + + \ No newline at end of file diff --git a/apps/posts/package.json b/apps/posts/package.json new file mode 100644 index 0000000000..a2a03b016c --- /dev/null +++ b/apps/posts/package.json @@ -0,0 +1,55 @@ +{ + "name": "@tryghost/posts", + "version": "0.0.0", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/TryGhost/Ghost/tree/main/apps/posts" + }, + "author": "Ghost Foundation", + "files": [ + "LICENSE", + "README.md", + "dist/" + ], + "main": "./dist/posts.umd.cjs", + "module": "./dist/posts.js", + "private": true, + "scripts": { + "dev": "vite build --watch", + "dev:start": "vite", + "build": "tsc && vite build", + "lint": "yarn run lint:code && yarn run lint:test", + "lint:code": "eslint --ext .js,.ts,.cjs,.tsx --cache src", + "lint:test": "eslint -c test/.eslintrc.cjs --ext .js,.ts,.cjs,.tsx --cache test", + "preview": "vite preview" + }, + "devDependencies": { + "@testing-library/react": "14.3.1", + "@tryghost/shade": "0.0.0", + "@tryghost/admin-x-framework": "0.0.0", + "@types/react": "18.3.3", + "@types/react-dom": "18.3.0", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "nx": { + "targets": { + "dev": { + "dependsOn": [ + "^build" + ] + }, + "test:unit": { + "dependsOn": [ + "^build" + ] + }, + "test:acceptance": { + "dependsOn": [ + "^build" + ] + } + } + } +} \ No newline at end of file diff --git a/apps/posts/playwright.config.mjs b/apps/posts/playwright.config.mjs new file mode 100644 index 0000000000..8fa59553e5 --- /dev/null +++ b/apps/posts/playwright.config.mjs @@ -0,0 +1,3 @@ +import {adminXPlaywrightConfig} from '@tryghost/admin-x-framework/playwright'; + +export default adminXPlaywrightConfig(); diff --git a/apps/posts/postcss.config.cjs b/apps/posts/postcss.config.cjs new file mode 100644 index 0000000000..b83c9032b2 --- /dev/null +++ b/apps/posts/postcss.config.cjs @@ -0,0 +1 @@ +module.exports = require('@tryghost/shade/postcss.config.cjs'); diff --git a/apps/posts/src/App.tsx b/apps/posts/src/App.tsx new file mode 100644 index 0000000000..c5010a29ad --- /dev/null +++ b/apps/posts/src/App.tsx @@ -0,0 +1,23 @@ +import PostAnalytics from './pages/PostAnalytics'; +import {FrameworkProvider, TopLevelFrameworkProps} from '@tryghost/admin-x-framework'; +import {RoutingProvider} from '@tryghost/admin-x-framework/routing'; +import {ShadeApp, ShadeAppProps} from '@tryghost/shade'; + +interface AppProps { + framework: TopLevelFrameworkProps; + designSystem: ShadeAppProps; +} + +const App: React.FC = ({framework, designSystem}) => { + return ( + + + + + + + + ); +}; + +export default App; diff --git a/apps/posts/src/components/layout/Header.tsx b/apps/posts/src/components/layout/Header.tsx new file mode 100644 index 0000000000..5fd1ba2126 --- /dev/null +++ b/apps/posts/src/components/layout/Header.tsx @@ -0,0 +1,26 @@ +import {Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, H1} from '@tryghost/shade'; + +const Header = () => { + return ( +
+ + + + + Posts + + + + + + Analytics + + + + +

The Evolution of Basketball: From Pastime to Professional and One of the Most Popular Sports

+
+ ); +}; + +export default Header; diff --git a/apps/posts/src/components/post-analytics/Overview.tsx b/apps/posts/src/components/post-analytics/Overview.tsx new file mode 100644 index 0000000000..73f086c174 --- /dev/null +++ b/apps/posts/src/components/post-analytics/Overview.tsx @@ -0,0 +1,17 @@ +import ClickPerformance from './overview/ClickPerformance'; +import Conversions from './overview/Conversions'; +import Feedback from './overview/Feedback'; +import NewsletterPerformance from './overview/NewsletterPerformance'; + +const Overview = () => { + return ( +
+ + + + +
+ ); +}; + +export default Overview; diff --git a/apps/posts/src/components/post-analytics/overview/ClickPerformance.tsx b/apps/posts/src/components/post-analytics/overview/ClickPerformance.tsx new file mode 100644 index 0000000000..50189e293a --- /dev/null +++ b/apps/posts/src/components/post-analytics/overview/ClickPerformance.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@tryghost/shade'; + +interface ClickPerformanceProps extends React.ComponentProps {}; + +const ClickPerformance: React.FC = (props) => { + return ( + + + Click performance + + Links in this newsletter + + + + Card contents + + + ); +}; + +export default ClickPerformance; diff --git a/apps/posts/src/components/post-analytics/overview/Conversions.tsx b/apps/posts/src/components/post-analytics/overview/Conversions.tsx new file mode 100644 index 0000000000..ec3f2ff00b --- /dev/null +++ b/apps/posts/src/components/post-analytics/overview/Conversions.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@tryghost/shade'; + +interface ConversionsProps extends React.ComponentProps {}; + +const Conversions: React.FC = (props) => { + return ( + + + Conversions + + 3 members signed up on this post + + + + Card contents + + + ); +}; + +export default Conversions; diff --git a/apps/posts/src/components/post-analytics/overview/Feedback.tsx b/apps/posts/src/components/post-analytics/overview/Feedback.tsx new file mode 100644 index 0000000000..0e0b7d48e8 --- /dev/null +++ b/apps/posts/src/components/post-analytics/overview/Feedback.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@tryghost/shade'; + +interface FeedbackProps extends React.ComponentProps {}; + +const Feedback: React.FC = (props) => { + return ( + + + Feedback + + 188 reactions + + + + Card contents + + + ); +}; + +export default Feedback; diff --git a/apps/posts/src/components/post-analytics/overview/NewsletterPerformance.tsx b/apps/posts/src/components/post-analytics/overview/NewsletterPerformance.tsx new file mode 100644 index 0000000000..9f4397cf8b --- /dev/null +++ b/apps/posts/src/components/post-analytics/overview/NewsletterPerformance.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import {Card, CardContent, CardDescription, CardHeader, CardTitle} from '@tryghost/shade'; + +interface NewsletterPerformanceProps extends React.ComponentProps {}; + +const NewsletterPerformance: React.FC = (props) => { + return ( + + + Newsletter performance + + Sent 19 Sept 2024 + + + + Card contents + + + ); +}; + +export default NewsletterPerformance; diff --git a/apps/posts/src/index.tsx b/apps/posts/src/index.tsx new file mode 100644 index 0000000000..cb20f2b589 --- /dev/null +++ b/apps/posts/src/index.tsx @@ -0,0 +1,6 @@ +import './styles/index.css'; +import App from './App'; + +export { + App as AdminXApp +}; diff --git a/apps/posts/src/pages/PostAnalytics.tsx b/apps/posts/src/pages/PostAnalytics.tsx new file mode 100644 index 0000000000..34a0d75610 --- /dev/null +++ b/apps/posts/src/pages/PostAnalytics.tsx @@ -0,0 +1,47 @@ +import Header from '../components/layout/Header'; +import Overview from '../components/post-analytics/Overview'; +import {Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, Icon, Page, Tabs, TabsContent, TabsList, TabsTrigger} from '@tryghost/shade'; + +const PostAnalytics = () => { + return ( + +
+ +
+ + Overview + Newsletter + +
+ + + + + + + + Edit post + ⇧⌘E + + + View in browser + ⇧⌘O + + + Delete + + +
+
+ + + + + Newsletter details + +
+ + ); +}; + +export default PostAnalytics; diff --git a/apps/posts/src/standalone.tsx b/apps/posts/src/standalone.tsx new file mode 100644 index 0000000000..561d48855f --- /dev/null +++ b/apps/posts/src/standalone.tsx @@ -0,0 +1,5 @@ +import './styles/index.css'; +import App from './App'; +import renderShadeApp from '@tryghost/admin-x-framework/test/render-shade'; + +renderShadeApp(App, {}); diff --git a/apps/posts/src/styles/index.css b/apps/posts/src/styles/index.css new file mode 100644 index 0000000000..5dda7e8943 --- /dev/null +++ b/apps/posts/src/styles/index.css @@ -0,0 +1 @@ +@import "@tryghost/shade/styles.css"; diff --git a/apps/posts/tailwind.config.cjs b/apps/posts/tailwind.config.cjs new file mode 100644 index 0000000000..c957a7bc92 --- /dev/null +++ b/apps/posts/tailwind.config.cjs @@ -0,0 +1,6 @@ +import shadePreset from '@tryghost/shade/tailwind.cjs'; + +module.exports = { + presets: [shadePreset('.shade')], + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', '../../node_modules/@tryghost/shade/es/**/*.{js,ts,jsx,tsx}'] +}; diff --git a/apps/posts/test/.eslintrc.cjs b/apps/posts/test/.eslintrc.cjs new file mode 100644 index 0000000000..42f8e77355 --- /dev/null +++ b/apps/posts/test/.eslintrc.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: ['ghost'], + extends: [ + 'plugin:ghost/ts-test' + ] +}; diff --git a/apps/posts/tsconfig.json b/apps/posts/tsconfig.json new file mode 100644 index 0000000000..3d679458a4 --- /dev/null +++ b/apps/posts/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src", "test"] +} diff --git a/apps/posts/vite.config.mjs b/apps/posts/vite.config.mjs new file mode 100644 index 0000000000..ae5b996d87 --- /dev/null +++ b/apps/posts/vite.config.mjs @@ -0,0 +1,10 @@ +import adminXViteConfig from '@tryghost/admin-x-framework/vite'; +import pkg from './package.json'; +import {resolve} from 'path'; + +export default (function viteConfig() { + return adminXViteConfig({ + packageName: pkg.name, + entry: resolve(__dirname, 'src/index.tsx') + }); +}); diff --git a/apps/shade/.storybook/main.tsx b/apps/shade/.storybook/main.tsx index 2bcb72b912..a4b867d2ed 100644 --- a/apps/shade/.storybook/main.tsx +++ b/apps/shade/.storybook/main.tsx @@ -25,6 +25,6 @@ const config: StorybookConfig = { crypto: require.resolve('rollup-plugin-node-builtins') } return config; - } + }, }; export default config; diff --git a/apps/shade/.storybook/manager.tsx b/apps/shade/.storybook/manager.tsx index 720f96d65c..7052b1b7f9 100644 --- a/apps/shade/.storybook/manager.tsx +++ b/apps/shade/.storybook/manager.tsx @@ -3,4 +3,4 @@ import shadeTheme from './shade-theme'; addons.setConfig({ theme: shadeTheme -}); \ No newline at end of file +}); diff --git a/apps/shade/.storybook/preview.tsx b/apps/shade/.storybook/preview.tsx index 5dd71eed99..c5c2827633 100644 --- a/apps/shade/.storybook/preview.tsx +++ b/apps/shade/.storybook/preview.tsx @@ -59,7 +59,9 @@ const preview: Preview = { options: { storySort: { method: 'alphabetical', - order: ['Welcome', 'Adding components', 'Component usage', 'Conventions', 'Icons', 'Components', 'Meta', 'Experimental'], + order: [ + 'Welcome', 'Adding components', 'Component usage', 'Conventions', 'Icons', + 'Components', 'Layout', 'Experimental', 'Meta'], }, }, docs: { @@ -76,7 +78,7 @@ const preview: Preview = { let {scheme} = context.globals; return ( -
{ darkMode: boolean; - // fetchKoenigLexical: FetchKoenigLexical; + fetchKoenigLexical: null; } -const ShadeApp: React.FC = ({darkMode, className, children, ...props}) => { +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const ShadeApp: React.FC = ({darkMode, fetchKoenigLexical, className, children, ...props}) => { const appClassName = clsx( - 'shade-base', + 'shade', darkMode && 'dark', className ); diff --git a/apps/shade/src/boilerplate.tsx b/apps/shade/src/boilerplate.tsx index fcccdbe5ae..6aa687cc9d 100644 --- a/apps/shade/src/boilerplate.tsx +++ b/apps/shade/src/boilerplate.tsx @@ -12,4 +12,4 @@ const BoilerPlate: React.FC = ({children}) => { ); }; -export default BoilerPlate; \ No newline at end of file +export default BoilerPlate; diff --git a/apps/shade/src/components/layout/heading.stories.tsx b/apps/shade/src/components/layout/heading.stories.tsx new file mode 100644 index 0000000000..d5c524a1bb --- /dev/null +++ b/apps/shade/src/components/layout/heading.stories.tsx @@ -0,0 +1,42 @@ +import type {Meta, StoryObj} from '@storybook/react'; +import {H1, H2, H3, H4, HeadingProps} from './heading'; + +const meta = { + title: 'Layout / Heading', + tags: ['autodocs'] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const HeadingOne = { + render: (args: Story['args']) => { + return ( +

The Joke Tax Chronicles

+ ); + } +}; + +export const HeadingTwo = { + render: (args: Story['args']) => { + return ( +

The Plan

+ ); + } +}; + +export const HeadingThree = { + render: (args: Story['args']) => { + return ( +

The Joke Tax

+ ); + } +}; + +export const HeadingFour = { + render: (args: Story['args']) => { + return ( +

Jokester Revolt

+ ); + } +}; diff --git a/apps/shade/src/components/layout/heading.tsx b/apps/shade/src/components/layout/heading.tsx new file mode 100644 index 0000000000..9d97d187b9 --- /dev/null +++ b/apps/shade/src/components/layout/heading.tsx @@ -0,0 +1,60 @@ +import {cn} from '@/lib/utils'; +import * as React from 'react'; + +export interface HeadingProps + extends React.HTMLAttributes {} + +const H1 = React.forwardRef( + ({className, ...props}, ref) => { + return ( +

+ ); + } +); +H1.displayName = 'H1'; + +const H2 = React.forwardRef( + ({className, ...props}, ref) => { + return ( +

+ ); + } +); +H2.displayName = 'H2'; + +const H3 = React.forwardRef( + ({className, ...props}, ref) => { + return ( +

+ ); + } +); +H3.displayName = 'H3'; + +const H4 = React.forwardRef( + ({className, ...props}, ref) => { + return ( +

+ ); + } +); +H4.displayName = 'H4'; + +export { + H1, + H2, + H3, + H4 +}; diff --git a/apps/shade/src/components/layout/page.stories.tsx b/apps/shade/src/components/layout/page.stories.tsx new file mode 100644 index 0000000000..8112e3d59b --- /dev/null +++ b/apps/shade/src/components/layout/page.stories.tsx @@ -0,0 +1,21 @@ +import type {Meta, StoryObj} from '@storybook/react'; +import {Page} from './page'; + +const meta = { + title: 'Layout / Page', + component: Page, + tags: ['autodocs'] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: ( + <> + Page container with a max width of max-w-content + + ) + } +}; diff --git a/apps/shade/src/components/layout/page.tsx b/apps/shade/src/components/layout/page.tsx new file mode 100644 index 0000000000..c11775bed3 --- /dev/null +++ b/apps/shade/src/components/layout/page.tsx @@ -0,0 +1,21 @@ +import {cn} from '@/lib/utils'; +import * as React from 'react'; + +export interface PageProps + extends React.HTMLAttributes {} + +const Page = React.forwardRef( + ({className, ...props}, ref) => { + return ( +
+ ); + } +); + +Page.displayName = 'Page'; + +export {Page}; diff --git a/apps/shade/src/components/ui/breadcrumb.stories.tsx b/apps/shade/src/components/ui/breadcrumb.stories.tsx new file mode 100644 index 0000000000..e9dff42d3a --- /dev/null +++ b/apps/shade/src/components/ui/breadcrumb.stories.tsx @@ -0,0 +1,31 @@ +import type {Meta, StoryObj} from '@storybook/react'; +import {Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator} from './breadcrumb'; + +const meta = { + title: 'Components / Breadcrumb', + component: Breadcrumb, + tags: ['autodocs'] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + children: ( + + + Home + + + + Components + + + + Breadcrumb + + + ) + } +}; diff --git a/apps/shade/src/components/ui/breadcrumb.tsx b/apps/shade/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000000..bcfe5eaae6 --- /dev/null +++ b/apps/shade/src/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from 'react'; +import {Slot} from '@radix-ui/react-slot'; +import {ChevronRight, MoreHorizontal} from 'lucide-react'; + +import {cn} from '@/lib/utils'; + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<'nav'> & { + separator?: React.ReactNode + } +>(({...props}, ref) =>