Added ActivityPub playground (#20081)

ref MOM61

- Adds admin-x react app we’ll use as ActivityPub playground to the
sidebar nav behind the feature flag.
- Wired up routing to Ember
- Setup the project as `admin-x-activitypub`

---------

Co-authored-by: Ronald Langeveld <hi@ronaldlangeveld.com>
This commit is contained in:
Djordje Vlaisavljevic 2024-04-25 09:44:29 +01:00 committed by GitHub
parent af02ca7044
commit 7a3bbfde10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 307 additions and 5 deletions

View File

@ -66,7 +66,7 @@ const COMMAND_TYPESCRIPT = {
env: {}
};
const adminXApps = '@tryghost/admin-x-demo,@tryghost/admin-x-settings';
const adminXApps = '@tryghost/admin-x-demo,@tryghost/admin-x-settings,@tryghost/admin-x-activitypub';
const COMMANDS_ADMINX = [{
name: 'adminXDeps',
@ -77,7 +77,7 @@ const COMMANDS_ADMINX = [{
}, {
name: 'adminX',
command: `nx run-many --projects=${adminXApps} --parallel=${adminXApps.length} --targets=dev`,
cwd: path.resolve(__dirname, '../../apps/admin-x-settings'),
cwd: path.resolve(__dirname, '../../apps/admin-x-settings', '../../apps/admin-x-activitypub'),
prefixColor: '#C35831',
env: {}
}];

View File

@ -0,0 +1 @@
tailwind.config.cjs

View File

@ -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'}]
}
};

3
apps/admin-x-activitypub/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
dist
playwright-report
test-results

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AdminX Standalone</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/standalone.tsx"></script>
</body>
</html>

View File

@ -0,0 +1,67 @@
{
"name": "@tryghost/admin-x-activitypub",
"version": "0.0.1",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/TryGhost/Ghost/tree/main/apps/admin-x-activitypub"
},
"author": "Ghost Foundation",
"files": [
"LICENSE",
"README.md",
"dist/"
],
"main": "./dist/admin-x-activitypub.umd.cjs",
"module": "./dist/admin-x-activitypub.js",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"scripts": {
"dev": "vite build --watch",
"dev:start": "vite",
"build": "concurrently \"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",
"test:unit": "vitest run",
"test:acceptance": "NODE_OPTIONS='--experimental-specifier-resolution=node --no-warnings' VITE_TEST=true playwright test",
"test:acceptance:slowmo": "TIMEOUT=100000 PLAYWRIGHT_SLOWMO=100 yarn test:acceptance --headed",
"test:acceptance:full": "ALL_BROWSERS=1 yarn test:acceptance",
"preview": "vite preview"
},
"devDependencies": {
"@testing-library/react": "14.1.0",
"@tryghost/admin-x-design-system": "0.0.0",
"@tryghost/admin-x-framework": "0.0.0",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"nx": {
"targets": {
"build": {
"dependsOn": [
"build",
{"projects": ["@tryghost/admin-x-design-system", "@tryghost/admin-x-framework"], "target": "build"}
]
},
"test:unit": {
"dependsOn": [
"build",
"test:unit",
{"projects": ["@tryghost/admin-x-design-system", "@tryghost/admin-x-framework"], "target": "build"}
]
},
"test:acceptance": {
"dependsOn": [
"build",
"test:acceptance",
{"projects": ["@tryghost/admin-x-design-system", "@tryghost/admin-x-framework"], "target": "build"}
]
}
}
}
}

View File

@ -0,0 +1,3 @@
import {adminXPlaywrightConfig} from '@tryghost/admin-x-framework/playwright';
export default adminXPlaywrightConfig();

View File

@ -0,0 +1 @@
module.exports = require('@tryghost/admin-x-design-system/postcss.config.cjs');

View File

@ -0,0 +1,23 @@
import ListIndex from './components/ListIndex';
import {DesignSystemApp, DesignSystemAppProps} from '@tryghost/admin-x-design-system';
import {FrameworkProvider, TopLevelFrameworkProps} from '@tryghost/admin-x-framework';
import {RoutingProvider} from '@tryghost/admin-x-framework/routing';
interface AppProps {
framework: TopLevelFrameworkProps;
designSystem: DesignSystemAppProps;
}
const App: React.FC<AppProps> = ({framework, designSystem}) => {
return (
<FrameworkProvider {...framework}>
<RoutingProvider basePath='activitypub'>
<DesignSystemApp className='admin-x-activitypub' {...designSystem}>
<ListIndex />
</DesignSystemApp>
</RoutingProvider>
</FrameworkProvider>
);
};
export default App;

View File

@ -0,0 +1,26 @@
const ListIndex = () => {
return (
<div className='mx-auto my-0 w-full max-w-3xl p-12'>
<h1 className='mb-6 text-black'>ActivityPub Demo</h1>
<div className='flex flex-col'>
<div className='mb-4 flex flex-col'>
<h2 className='mb-2 text-2xl text-black'>This is a post title</h2>
<p className='mb-2 text-lg text-grey-950'>This is some very short post content</p>
<p className='text-md text-grey-700'>Publish McPublisher</p>
</div>
<div className='mb-4 flex flex-col'>
<h2 className='mb-2 text-2xl text-black'>This is a post title</h2>
<p className='mb-2 text-lg text-grey-950'>This is some very short post content</p>
<p className='text-md text-grey-700'>Publish McPublisher</p>
</div>
<div className='mb-4 flex flex-col'>
<h2 className='mb-2 text-2xl text-black'>This is a post title</h2>
<p className='mb-2 text-lg text-grey-950'>This is some very short post content</p>
<p className='text-md text-grey-700'>Publish McPublisher</p>
</div>
</div>
</div>
);
};
export default ListIndex;

View File

@ -0,0 +1,6 @@
import './styles/index.css';
import App from './App';
export {
App as AdminXApp
};

View File

@ -0,0 +1,5 @@
import './styles/index.css';
import App from './App.tsx';
import renderStandaloneApp from '@tryghost/admin-x-framework/test/render';
renderStandaloneApp(App, {});

View File

@ -0,0 +1 @@
@import '@tryghost/admin-x-design-system/styles.css';

View File

@ -0,0 +1,6 @@
const adminXPreset = require('@tryghost/admin-x-design-system/tailwind.cjs');
module.exports = {
presets: [adminXPreset('.admin-x-activitypub')],
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', '../../node_modules/@tryghost/admin-x-design-system/es/**/*.{js,ts,jsx,tsx}']
};

View File

@ -0,0 +1,6 @@
module.exports = {
plugins: ['ghost'],
extends: [
'plugin:ghost/ts-test'
]
};

View File

@ -0,0 +1,10 @@
import {expect, test} from '@playwright/test';
// import {mockApi, responseFixtures} from '@tryghost/admin-x-framework/test/acceptance';
test.describe('Demo', async () => {
test('Renders the list page', async ({page}) => {
await page.goto('/');
await expect(page.locator('body')).toContainText('ActivityPub Demo');
});
});

View File

@ -0,0 +1,10 @@
import ListIndex from '../../src/components/ListIndex';
import {render, screen} from '@testing-library/react';
describe('Demo', function () {
it('renders a component', async function () {
render(<ListIndex/>);
expect(screen.getAllByRole('heading')[0].textContent).toEqual('ActivityPub Demo');
});
});

View File

@ -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"]
}

View File

@ -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')
});
});

View File

@ -0,0 +1 @@
<div {{react-render this.ReactComponent}}></div>

View File

@ -0,0 +1,8 @@
import AdminXComponent from './admin-x-component';
import {inject as service} from '@ember/service';
export default class AdminXActivityPub extends AdminXComponent {
@service upgradeStatus;
static packageName = '@tryghost/admin-x-activitypub';
}

View File

@ -123,6 +123,11 @@
<LinkTo @route="demo-x" @current-when="demo-x">{{svg-jar "star"}}AdminX Demo</LinkTo>
</li>
{{/if}}
{{#if (feature "ActivityPub")}}
<li>
<LinkTo @route="activitypub-x" @current-when="activitypub-x">{{svg-jar "star"}}ActivityPub Demo</LinkTo>
</li>
{{/if}}
</ul>
{{#if this.session.user.isOwnerOnly}}

View File

@ -0,0 +1,6 @@
import Controller from '@ember/controller';
import {inject as service} from '@ember/service';
export default class ActivitypubXController extends Controller {
@service upgradeStatus;
}

View File

@ -56,6 +56,10 @@ Router.map(function () {
this.route('settings-x', {path: '/*sub'});
});
this.route('activitypub-x',{path: '/activitypub'}, function () {
this.route('activitypub-x', {path: '/*sub'});
});
// testing websockets
this.route('websockets');

View File

@ -0,0 +1,3 @@
import AuthenticatedRoute from 'ghost-admin/routes/authenticated';
export default class AcitvitypubXRoute extends AuthenticatedRoute {}

View File

@ -82,6 +82,7 @@ export default class FeatureService extends Service {
@feature('portalImprovements') portalImprovements;
@feature('onboardingChecklist') onboardingChecklist;
@feature('membersSpamPrevention') membersSpamPrevention;
@feature('ActivityPub') ActivityPub;
@feature('internalLinking') internalLinking;
_user = null;

View File

@ -0,0 +1 @@
<AdminX::Activitypub />

View File

@ -6,7 +6,7 @@ const fs = require('fs');
const path = require('path');
const camelCase = require('lodash/camelCase');
const adminXApps = ['admin-x-demo', 'admin-x-settings'];
const adminXApps = ['admin-x-demo', 'admin-x-settings', 'admin-x-activitypub'];
function generateHash(filePath) {
const fileContents = fs.readFileSync(filePath, 'utf8');

View File

@ -181,7 +181,8 @@
{
"projects": [
"@tryghost/admin-x-demo",
"@tryghost/admin-x-settings"
"@tryghost/admin-x-settings",
"@tryghost/admin-x-activitypub"
],
"target": "build"
}
@ -193,7 +194,8 @@
{
"projects": [
"@tryghost/admin-x-demo",
"@tryghost/admin-x-settings"
"@tryghost/admin-x-settings",
"@tryghost/admin-x-activitypub"
],
"target": "build"
}

View File

@ -53,6 +53,7 @@ const ALPHA_FEATURES = [
// 'adminXOffers',
'adminXDemo',
'membersSpamPrevention',
'ActivityPub',
'internalLinking'
];