Merge branch 'main' into patch-1

This commit is contained in:
Pratyush Joshi 2023-10-26 17:48:06 +05:30 committed by GitHub
commit 6871e3993f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
169 changed files with 1281 additions and 945 deletions

View File

@ -217,6 +217,29 @@ jobs:
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
job_i18n:
runs-on: ubuntu-latest
needs: [job_get_metadata, job_install_deps]
name: i18n
if: |
needs.job_get_metadata.outputs.changed_comments_ui == 'true'
|| needs.job_get_metadata.outputs.changed_signup_form == 'true'
|| needs.job_get_metadata.outputs.changed_portal == 'true'
|| needs.job_get_metadata.outputs.changed_core == 'true'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: "18.12.1"
- name: Restore caches
uses: ./.github/actions/restore-cache
env:
DEPENDENCY_CACHE_KEY: ${{ needs.job_install_deps.outputs.dependency_cache_key }}
- name: Run i18n tests
run: yarn nx run @tryghost/i18n:test
job_admin-tests:
runs-on: ubuntu-latest
needs: [job_get_metadata, job_install_deps]
@ -264,7 +287,7 @@ jobs:
runs-on:
labels: ubuntu-latest-4-cores
needs: [job_get_metadata, job_install_deps]
if: needs.job_get_metadata.outputs.is_main == 'true' || needs.job_get_metadata.outputs.has_browser_tests_label == 'true'
if: needs.job_get_metadata.outputs.changed_any_code == 'true' && (needs.job_get_metadata.outputs.is_main == 'true' || needs.job_get_metadata.outputs.has_browser_tests_label == 'true')
concurrency:
group: ${{ github.workflow }}
steps:
@ -750,6 +773,22 @@ jobs:
- run: mv ghost-*.tgz ghost.tgz
working-directory: ghost/core
- name: Install latest v4
run: |
DIR=$(mktemp -d)
echo "V4_DIR=$DIR" >> $GITHUB_ENV
ghost install v4 --local -d $DIR
- uses: actions/setup-node@v3
env:
FORCE_COLOR: 0
with:
node-version: '18.12.1'
- name: Update from v4
run: |
ghost update -f -d $V4_DIR --archive $(pwd)/ghost/core/ghost.tgz
- name: Clean Install
run: |
DIR=$(mktemp -d)
@ -761,12 +800,6 @@ jobs:
ghost install local -d $DIR
ghost update -d $DIR --archive $(pwd)/ghost/core/ghost.tgz
- name: Update from latest v4
run: |
DIR=$(mktemp -d)
ghost install v4 --local -d $DIR
ghost update -f -d $DIR --archive $(pwd)/ghost/core/ghost.tgz
- name: Print debug logs
if: failure()
run: |
@ -832,11 +865,13 @@ jobs:
job_get_metadata,
job_install_deps,
job_lint,
job_i18n,
job_ghost-cli,
job_admin-tests,
job_unit-tests,
job_database-tests,
job_regression-tests,
job_browser-tests,
job_admin_x_settings,
job_comments_ui,
job_signup_form,

View File

@ -30,7 +30,7 @@
</a>
</p>
<p align="center">
Love open source? <a href="https://careers.ghost.org">We're hiring</a> JavaScript engineers to work on Ghost full-time.
Love open source? <a href="https://careers.ghost.org/devops-engineer">We're hiring</a> DevOps engineers to work on Ghost full-time.
</p>
&nbsp;

View File

@ -85,8 +85,8 @@
"rollup-plugin-node-builtins": "2.1.2",
"storybook": "7.4.0",
"stylelint": "15.10.3",
"tailwindcss": "3.3.3",
"vite": "4.4.11",
"tailwindcss": "3.3.5",
"vite": "4.5.0",
"vite-plugin-css-injected-by-js": "^3.3.0",
"vite-plugin-svgr": "3.3.0",
"vitest": "0.34.3"

View File

@ -43,8 +43,9 @@ const queryClient = new QueryClient({
function App({ghostVersion, officialThemes, zapierTemplates, externalNavigate, darkMode = false, unsplashConfig, fetchKoenigLexical, sentryDSN, onUpdate, onInvalidate, onDelete, upgradeStatus}: AppProps) {
const appClassName = clsx(
'admin-x-settings admin-x-base h-[100vh] w-full overflow-y-auto overflow-x-hidden',
'admin-x-settings admin-x-base',
darkMode && 'dark'
//'!h-[calc(100vh-55px)] w-full overflow-y-auto overflow-x-hidden tablet:!h-[100vh]'
);
return (
@ -57,8 +58,8 @@ function App({ghostVersion, officialThemes, zapierTemplates, externalNavigate, d
<GlobalDirtyStateProvider>
<DesignSystemProvider>
<div className={appClassName} id="admin-x-root" style={{
height: '100vh',
width: '100%'
// height: '100vh',
// width: '100%'
}}
>
<Toaster />

View File

@ -12,11 +12,11 @@ import {useGlobalData} from './components/providers/GlobalDataProvider';
const Page: React.FC<{children: ReactNode}> = ({children}) => {
return <>
<div className='relative z-20 px-6 py-4 tablet:fixed'>
<div className='sticky top-0 z-30 px-[5vmin] py-4 tablet:fixed tablet:px-6'>
<ExitSettingsButton />
</div>
<div className="mx-auto flex max-w-[1080px] flex-col px-[5vmin] py-[12vmin] tablet:flex-row tablet:items-start tablet:gap-x-10 tablet:py-[8vmin]" id="admin-x-settings-content">
<div className="mx-auto flex max-w-[1080px] flex-col px-[5vmin] pb-[12vmin] tablet:flex-row tablet:items-start tablet:gap-x-10 tablet:py-[8vmin]" id="admin-x-settings-content">
{children}
</div>
</>;
@ -54,18 +54,15 @@ const MainContent: React.FC = () => {
return (
<Page>
{loadingModal && <div className={`fixed inset-0 z-40 h-[100vh] w-[100vw] ${topLevelBackdropClasses}`} />}
{loadingModal && <div className={`fixed inset-0 z-40 h-[calc(100vh-55px)] w-[100vw] tablet:h-[100vh] ${topLevelBackdropClasses}`} />}
{/* Sidebar */}
<div className="sticky top-[-47px] z-30 min-w-[260px] grow-0 md:top-[-52px] tablet:fixed tablet:top-[8vmin] tablet:basis-[260px]">
<div className='-mx-6 h-[84px] bg-white px-6 dark:bg-black tablet:m-0 tablet:bg-transparent tablet:p-0'>
<Heading>Settings</Heading>
</div>
<div className="relative mt-[-32px] w-full overflow-x-hidden bg-white dark:bg-black">
<div className="sticky -top-px z-20 mt-[-55px] min-w-[260px] grow-0 bg-white pt-[52px] dark:bg-black tablet:fixed tablet:top-[8vmin] tablet:mt-0 tablet:basis-[260px] tablet:pt-0">
<div className="relative w-full bg-white dark:bg-black">
<Sidebar />
</div>
</div>
<div className="relative flex-auto pt-[10vmin] tablet:ml-[300px] tablet:pt-[94px]">
<div className="relative flex-auto pt-[10vmin] tablet:ml-[330px] tablet:pt-0">
<Settings />
</div>
</Page>

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M16.171875 11.25A4.921875 4.921875 0 1 1 11.25 6.328125 4.921875 4.921875 0 0 1 16.171875 11.25Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M16.171875 11.25v2.109375a2.8125 2.8125 0 0 0 5.625 0V11.25a10.5459375 10.5459375 0 1 0 -4.21875 8.4375" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 532 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-0.75 -0.75 24 24" height="24" width="24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M0.9375 20.0625h1.8403125" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M19.723125 20.0625H21.5625" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M15.02625 20.0625h1.8403125" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M10.3303125 20.0625h1.839375" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M5.6343749999999995 20.0625h1.839375" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m0.9375 16.53 4.790625 -6.511875a3.1565625 3.1565625 0 0 1 3.1753125 -1.2225000000000001l4.685625 0.9590624999999999a3.1565625 3.1565625 0 0 0 3.17625 -1.2215624999999999l4.790625 -6.511875" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-0.75 -0.75 24 24" height="24" width="24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M17.578125 4.21875H2.109375A1.40625 1.40625 0 0 0 0.703125 5.625v8.4375a1.40625 1.40625 0 0 0 1.40625 1.40625h15.46875a1.40625 1.40625 0 0 0 1.40625 -1.40625V5.625a1.40625 1.40625 0 0 0 -1.40625 -1.40625Z" stroke-width="1.5"></path><path stroke="currentColor" d="M3.8671875 7.734375a0.3515625 0.3515625 0 1 1 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" d="M3.8671875 7.734375a0.3515625 0.3515625 0 1 0 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" d="M15.8203125 12.65625a0.3515625 0.3515625 0 0 1 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" d="M15.8203125 12.65625a0.3515625 0.3515625 0 0 0 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M9.84375 12.65625a2.8125 2.8125 0 1 0 0 -5.625 2.8125 2.8125 0 0 0 0 5.625Z" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M21.796875 8.4375v8.4375a1.40625 1.40625 0 0 1 -1.40625 1.40625H4.921875" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="m7.152187499999999 4.21875 -6.0375000000000005 6.0365625000000005a1.40625 1.40625 0 0 0 0 1.9884375l6.0375000000000005 6.0375000000000005" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m15.347812499999998 4.21875 6.0375000000000005 6.0365625000000005a1.40625 1.40625 0 0 1 0 1.9884375l-6.0375000000000005 6.0375000000000005" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 608 B

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M10.546875 16.171875a5.625 5.625 0 1 0 11.25 0 5.625 5.625 0 1 0 -11.25 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m18.67875 14.536875 -2.7234374999999997 3.6309375000000004a0.705 0.705 0 0 1 -1.0603125 0.0759375l-1.40625 -1.40625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M7.734375 14.765625h-5.625a1.40625 1.40625 0 0 1 -1.40625 -1.40625v-11.25a1.40625 1.40625 0 0 1 1.40625 -1.40625h16.875a1.40625 1.40625 0 0 1 1.40625 1.40625V8.4375" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m20.0728125 1.21875 -7.635 5.8725000000000005a3.10125 3.10125 0 0 1 -3.781875 0L1.0209375 1.21875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1019 B

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="m1.40625 4.453125 19.6875 0 0 14.0625 -19.6875 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m20.7759375 4.96875 -7.635 5.8725000000000005a3.10125 3.10125 0 0 1 -3.781875 0L1.7240625 4.96875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 479 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.75 -0.75 24 24" height="24" width="24"><defs></defs><path d="M21.796875 12.421875v5.859375a0.9375 0.9375 0 0 1 -0.9375 0.9375H1.640625a0.9375 0.9375 0 0 1 -0.9375 -0.9375V8.671875a0.9375 0.9375 0 0 1 0.9375 -0.9375H8.4375" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M18.125625 13.300312499999999A5.15625 5.15625 0 1 1 21.5625 8.4375" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M14.6878125 8.4375a1.7184375 1.7184375 0 1 0 3.436875 0 1.7184375 1.7184375 0 1 0 -3.436875 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M18.1246875 8.4375A1.719375 1.719375 0 0 0 21.5625 8.4375" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m4.3706249999999995 10.9378125 0 5.077500000000001" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M16.996875 7.265625h-3.99375V5.475a0.9375 0.9375 0 0 1 0.9375 -1.03125h2.8125v-3.75h-4.059375c-3.684375 0 -4.378125 2.8125 -4.378125 4.55625v2.015625h-2.8125v3.75h2.8125v10.78125h4.6875v-10.78125h3.609375Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 420 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-0.75 -0.75 24 24" height="24" width="24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M6.140625 10.828125c-1.78125 0 -3.28125 1.5 -3.28125 3.28125 0 1.5 0.375 3 1.21875 4.3125l0.65625 1.125c0.84375 1.40625 2.4375 2.25 4.03125 2.25h6.1875c2.625 0 4.6875 -2.0625 4.6875 -4.6875v-6.84375c0 -0.9375 -0.75 -1.6875 -1.6875 -1.6875s-1.6875 0.75 -1.6875 1.6875v-0.9375c0 -0.9375 -0.75 -1.6875 -1.6875 -1.6875s-1.6875 0.75 -1.6875 1.6875v0.28125l0 -0.75c0 -0.9375 -0.75 -1.6875 -1.6875 -1.6875s-1.6875 0.75 -1.6875 1.6875l0 0.215625m0 0.5343749999999999 0 -0.5343749999999999m-3.375 4.753125000000001V2.390625c0 -0.9375 0.75 -1.6875 1.6875 -1.6875s1.6875 0.75 1.6875 1.6875l0 6.684375" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 827 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.75 -0.75 24 24" height="24" width="24"><g><path d="M12.01875 13.603125 14.399999999999999 11.25l1.65 0.440625a1.4625000000000001 1.4625000000000001 0 0 0 1.415625 -0.440625 1.4812500000000002 1.4812500000000002 0 0 0 0.346875 -1.396875l-0.440625 -1.640625 0.7687499999999999 -0.7125 1.65 0.440625A1.4625000000000001 1.4625000000000001 0 0 0 21.20625 7.5 1.4812500000000002 1.4812500000000002 0 0 0 21.5625 6.1125l-0.440625 -1.640625a2.203125 2.203125 0 0 0 -3.121875 -3.121875l-9.103125 9.13125a5.896875 5.896875 0 1 0 3.121875 3.121875Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M3.99375 16.725a1.78125 1.78125 0 1 0 3.5625 0 1.78125 1.78125 0 1 0 -3.5625 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></g></svg>

After

Width:  |  Height:  |  Size: 904 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-0.75 -0.75 24 24" height="24" width="24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M6.305625 0.703125h9.84375" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M14.743125000000001 7.734375V0.703125h-7.03125v7.03125L1.3959375 17.451562499999998A2.8125 2.8125 0 0 0 3.75 21.796875h14.95125a2.8125 2.8125 0 0 0 2.3578125 -4.3453124999999995L14.743125000000001 7.734375Z" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M4.9696875 11.953125h12.515625" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M13.336875000000001 16.171875h2.8125" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M14.743125000000001 14.765625v2.8125" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M14.743125000000001 3.515625h-2.8125" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M14.743125000000001 6.328125h-2.8125" stroke-width="1.5"></path><path stroke="currentColor" d="M6.305625 18.6328125a0.3515625 0.3515625 0 0 1 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" d="M6.305625 18.6328125a0.3515625 0.3515625 0 0 0 0 -0.703125" stroke-width="1.5"></path><g><path stroke="currentColor" d="M9.118125000000001 15.8203125a0.3515625 0.3515625 0 0 1 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" d="M9.118125000000001 15.8203125a0.3515625 0.3515625 0 0 0 0 -0.703125" stroke-width="1.5"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.75 -0.75 24 24" height="24" width="24"><g><path d="M2.109375 0.703125h8.4375s1.40625 0 1.40625 1.40625v8.4375s0 1.40625 -1.40625 1.40625h-8.4375s-1.40625 0 -1.40625 -1.40625v-8.4375s0 -1.40625 1.40625 -1.40625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M14.765625 10.546875h5.625a1.40625 1.40625 0 0 1 1.40625 1.40625v8.4375a1.40625 1.40625 0 0 1 -1.40625 1.40625h-8.4375a1.40625 1.40625 0 0 1 -1.40625 -1.40625v-5.625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m14.53125 16.875 3.28125 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><g><path d="m6.328125 3.515625 0 1.40625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m3.515625 4.921875 5.625 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M7.734375 4.921875s-1.40625 4.21875 -4.21875 4.21875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M6.328125 7.5a3.675 3.675 0 0 0 2.8125 1.621875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></g><path d="M14.53125 18.984375v-3.75a1.640625 1.640625 0 0 1 3.28125 0v3.75" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M21.478125 6.5184375 11.90625 1.5675a1.4465625 1.4465625 0 0 0 -1.3275 0L1.00875 6.5184375a0.5765625 0.5765625 0 0 0 0 1.025625l9.5709375 4.950937499999999a1.4465625 1.4465625 0 0 0 1.3275 0L21.478125 7.544062500000001a0.5775 0.5775 0 0 0 0 -1.025625Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m0.7106250000000001 11.953125 9.8690625 4.760625a1.4465625 1.4465625 0 0 0 1.3275 0l9.897187500000001 -4.760625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m0.7106250000000001 16.171875 9.8690625 4.760625a1.4465625 1.4465625 0 0 0 1.3275 0l9.897187500000001 -4.760625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 924 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-0.75 -0.75 24 24" height="24" width="24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M0.78375 9.6103125h1.3031249999999999c1.966875 0 3.855 -0.0684375 5.257499999999999 -1.4465625a7.5 7.5 0 0 0 2.2424999999999997 -5.2190625c0 -3.1734375 4.010624999999999 -1.6875 4.010624999999999 1.14375v3.646875a1.875 1.875 0 0 0 1.875 1.875h4.414687499999999c0.9806250000000001 0 1.8046875 0.7565625 1.8234375 1.7371874999999999 0.061875 3.1275 -0.459375 5.4028125 -1.7240625 7.824375 -0.729375 1.396875 -2.2434374999999998 2.175 -3.8184375000000004 2.1403125C5.2228125 21.065624999999997 6.6384375 19.21875 0.78375 19.21875" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 741 B

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M6.328125 14.296875H4.21875a3.515625 3.515625 0 0 1 0 -7.03125h2.109375Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M6.328125 14.296875a20.90625 20.90625 0 0 1 11.593125 3.5100000000000002l1.0631249999999999 0.70875V3.046875l-1.0631249999999999 0.70875A20.90625 20.90625 0 0 1 6.328125 7.265625Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m21.796875 9.375 0 2.8125" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M6.328125 14.296875A6.7865625 6.7865625 0 0 0 8.4375 19.21875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 906 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.75 -0.75 24 24" height="24" width="24"><defs></defs><title>module-three</title><path d="M2.109375 12.65625H8.4375s1.40625 0 1.40625 1.40625v6.328125s0 1.40625 -1.40625 1.40625H2.109375s-1.40625 0 -1.40625 -1.40625V14.0625s0 -1.40625 1.40625 -1.40625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M14.0625 12.65625h6.328125s1.40625 0 1.40625 1.40625v6.328125s0 1.40625 -1.40625 1.40625H14.0625s-1.40625 0 -1.40625 -1.40625V14.0625s0 -1.40625 1.40625 -1.40625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M8.0859375 0.703125h6.328125s1.40625 0 1.40625 1.40625V8.4375s0 1.40625 -1.40625 1.40625h-6.328125s-1.40625 0 -1.40625 -1.40625V2.109375s0 -1.40625 1.40625 -1.40625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 977 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.75 -0.75 24 24" height="24" width="24"><defs></defs><title>navigation-menu-4</title><path d="M2.109375 0.7059375h18.28125s1.40625 0 1.40625 1.40625v18.28125s0 1.40625 -1.40625 1.40625H2.109375s-1.40625 0 -1.40625 -1.40625v-18.28125s0 -1.40625 1.40625 -1.40625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m6.328125 7.0340625 9.84375 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m6.328125 11.252812500000001 9.84375 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m6.328125 15.471562500000001 9.84375 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 885 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-0.75 -0.75 24 24" height="24" width="24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M7.03125 0.703125H2.8125a1.40625 1.40625 0 0 0 -1.40625 1.40625v18.28125a1.40625 1.40625 0 0 0 1.40625 1.40625h4.21875a1.40625 1.40625 0 0 0 1.40625 -1.40625V2.109375A1.40625 1.40625 0 0 0 7.03125 0.703125Z" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m11.025 0.80625 3.9000000000000004 1.6125a1.415625 1.415625 0 0 1 0.7687499999999999 1.875L8.4375 20.390625" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m17.8875 5.428125 2.8125 3.121875a1.40625 1.40625 0 0 1 -0.09375 1.9875L8.26875 21.046875" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M1.40625 6.796875H8.4375" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M1.40625 12.890625H8.4375" stroke-width="1.5"></path><path stroke="currentColor" d="M4.86 18.9890625a0.3515625 0.3515625 0 0 1 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" d="M4.86 18.9890625a0.3515625 0.3515625 0 0 0 0 -0.703125" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-0.75 -0.75 24 24" height="24" width="24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M21.796875 8.4375a2.8125 2.8125 0 0 1 -2.8125 2.8125" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M9.375 7.03125h2.8125" stroke-width="1.5"></path><path stroke="currentColor" d="M5.9193750000000005 10.542187499999999a0.3515625 0.3515625 0 0 1 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" d="M5.9193750000000005 10.542187499999999a0.3515625 0.3515625 0 0 0 0 -0.703125" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M7.40625 4.10625C6.309375 2.11875 3.515625 2.109375 3.515625 2.109375l0.590625 4.10625A7.415625 7.415625 0 0 0 2.4375 9.140625H0.703125v5.625h2.334375a7.903124999999999 7.903124999999999 0 0 0 1.875 2.2218750000000003V19.6875a0.7125 0.7125 0 0 0 0.703125 0.703125H7.03125a0.7125 0.7125 0 0 0 0.703125 -0.703125v-1.1625a8.924999999999999 8.924999999999999 0 0 0 5.625 0V19.6875a0.7125 0.7125 0 0 0 0.703125 0.703125h1.40625a0.7125 0.7125 0 0 0 0.703125 -0.703125v-2.68125a7.445625 7.445625 0 0 0 2.8125 -5.75625c0 -6.0843750000000005 -6.609375 -8.803125000000001 -11.578125 -7.14375Z" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_718_1014)">
<path d="M16.5261 11.0917C16.3752 10.3419 16.0406 9.6412 15.5523 9.05252C15.064 8.46385 14.4372 8.00556 13.7282 7.71874M10.1882 7.75382C9.17274 8.18744 8.34628 8.97062 7.85872 9.96133C7.37116 10.952 7.25477 12.0847 7.53068 13.1538M9.63714 15.9655C10.3514 16.3922 11.1682 16.6168 12.0002 16.6154C12.749 16.6162 13.4866 16.4344 14.1493 16.0859C14.812 15.7373 15.3797 15.2325 15.8033 14.6151M14.0042 19.5877C15.072 19.3054 16.0682 18.801 16.9277 18.1074C17.7872 17.4139 18.4907 16.5467 18.9922 15.5627C19.4937 14.5786 19.7819 13.4998 19.8379 12.3968C19.8939 11.2938 19.7166 10.1913 19.3174 9.16151M17.1796 6.10613C15.7488 4.84585 13.9069 4.15158 12.0002 4.15382C10.0945 4.15064 8.25339 4.84434 6.8236 6.10428M4.71898 9.07013C4.29776 10.1172 4.10731 11.2428 4.16062 12.3702C4.21393 13.4976 4.50975 14.6002 5.02791 15.6029C5.54606 16.6056 6.27437 17.4847 7.16315 18.1803C8.05193 18.876 9.08027 19.3717 10.1781 19.6338" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
<path d="M8.23731 22.4216C9.41239 22.8462 10.6789 23.0769 11.9998 23.0769C17.0952 23.0769 21.3875 19.6366 22.6798 14.9511M6.19547 2.5634C4.58338 3.55458 3.25226 4.94244 2.3292 6.59448C1.40614 8.24652 0.921948 10.1076 0.922853 12C0.922853 15.2723 2.34162 18.2132 4.59855 20.2412M22.9373 10.236C22.0918 4.95602 17.517 0.923096 11.9998 0.923096C11.3629 0.923096 10.7379 0.976634 10.1305 1.08002" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</g>
<defs>
<clipPath id="clip0_718_1014">
<rect width="24" height="24"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M21.796875 14.765625v5.625a1.40625 1.40625 0 0 1 -1.40625 1.40625h-8.4375a1.40625 1.40625 0 0 1 -1.40625 -1.40625v-5.625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M21.796875 14.765625a1.40625 1.40625 0 0 0 -1.40625 -1.40625h-8.4375a1.40625 1.40625 0 0 0 -1.40625 1.40625L15.4265625 17.8125a1.40625 1.40625 0 0 0 1.490625 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M3.1640625 3.8671875a3.1640625 3.1640625 0 1 0 6.328125 0 3.1640625 3.1640625 0 1 0 -6.328125 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M13.0078125 3.1640625a2.4609375 2.4609375 0 1 0 4.921875 0 2.4609375 2.4609375 0 1 0 -4.921875 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M10.73625 10.542187499999999A5.6728125 5.6728125 0 0 0 0.703125 13.359375" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M19.6875 10.546875a4.20375 4.20375 0 0 0 -7.5346875 -2.578125" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.75 -0.75 24 24" height="24" width="24"><defs></defs><title>type-cursor</title><path d="M2.109375 6.32625h18.28125s1.40625 0 1.40625 1.40625v7.03125s0 1.40625 -1.40625 1.40625H2.109375s-1.40625 0 -1.40625 -1.40625v-7.03125s0 -1.40625 1.40625 -1.40625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m16.171875 17.57625 0 -12.65625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M11.953125 21.795a4.21875 4.21875 0 0 0 4.21875 -4.21875 4.21875 4.21875 0 0 0 4.21875 4.21875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M11.953125 0.70125a4.21875 4.21875 0 0 1 4.21875 4.21875 4.21875 4.21875 0 0 1 4.21875 -4.21875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 990 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.75 -0.75 24 24" height="24" width="24"><defs></defs><title>time-reverse</title><path d="m8.5903125 16.5028125 2.8115625 -2.8125 0.0009375 -4.6875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m13.273125 6.4246875 -3.75 -3.046875 4.21875 -2.578125" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M3.4753125 17.4375a9.2221875 9.2221875 0 1 0 6.1068750000000005 -14.0296875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M6.42375 4.6284375a9.346875 9.346875 0 0 0 -2.8528125 2.7525" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M2.19 10.78125a9.5728125 9.5728125 0 0 0 0.12187500000000001 3.9628125" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="-0.75 -0.75 24 24" height="24" width="24"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="M16.5440625 21.724687499999998 0.703125 0.703125l5.2528125 0L21.796875 21.724687499999998h-5.2528125Z" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m21.0515625 0.703125 -8.3503125 8.954062500000001" stroke-width="1.5"></path><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m1.4484374999999998 21.724687499999998 8.34375 -8.9475" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 631 B

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M0.703125 14.765625a7.03125 7.03125 0 1 0 14.0625 0 7.03125 7.03125 0 1 0 -14.0625 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M4.921875 13.359375a2.8125 2.8125 0 1 0 5.625 0 2.8125 2.8125 0 1 0 -5.625 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M12.3159375 20.0990625a5.1206249999999995 5.1206249999999995 0 0 0 -9.163124999999999 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M3.515625 4.921875v-2.8125a1.40625 1.40625 0 0 1 1.40625 -1.40625h9.9646875a1.40625 1.40625 0 0 1 0.99375 0.4115625l5.505 5.505a1.40625 1.40625 0 0 1 0.4115625 0.99375V20.390625a1.40625 1.40625 0 0 1 -1.40625 1.40625h-4.21875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M21.796875 7.734375h-5.625a1.40625 1.40625 0 0 1 -1.40625 -1.40625v-5.625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1 @@
<svg viewBox="-0.75 -0.75 24 24" xmlns="http://www.w3.org/2000/svg" height="24" width="24"><path d="M10.546875 16.171875a5.625 5.625 0 1 0 11.25 0 5.625 5.625 0 1 0 -11.25 0Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m18.658125000000002 16.171875 -2.48625 0 0 -2.4853125" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M9.838125 21.703125a10.5478125 10.5478125 0 1 1 11.866875 -11.85375" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M8.7084375 21.4884375C7.2825 19.3959375 6.328125 15.593437499999999 6.328125 11.25S7.2825 3.105 8.7084375 1.0115625" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m0.7265625 10.546875 8.9278125 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M2.8115625 4.921875 19.6875 4.921875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="m1.92 16.171875 5.814375 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path><path d="M13.7915625 1.0115625a15.9215625 15.9215625 0 0 1 2.15625 6.69" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -52,7 +52,7 @@ const Button: React.FC<ButtonProps> = ({
if (!unstyled) {
className = clsx(
'inline-flex items-center justify-center whitespace-nowrap rounded-sm text-sm transition',
'inline-flex items-center justify-center whitespace-nowrap rounded text-sm transition',
((link && color !== 'clear' && color !== 'black') || (!link && color !== 'clear')) ? 'font-bold' : 'font-semibold',
!link ? `${size === 'sm' ? ' h-7 px-3 ' : ' h-[34px] px-4 '}` : '',
(link && linkWithPadding) && '-m-1 p-1',

View File

@ -38,8 +38,11 @@ type HeadingLabelProps = {
level?: never,
grey?: boolean } & HeadingBaseProps & React.LabelHTMLAttributes<HTMLLabelElement>
export const Heading6Styles = 'text-2xs font-semibold uppercase tracking-wider';
export const Heading6StylesGrey = 'text-2xs font-semibold uppercase tracking-wider text-grey-800 dark:text-grey-500';
export const Heading6Styles = clsx('text-xs font-semibold tracking-normal');
export const Heading6StylesGrey = clsx(
Heading6Styles,
'text-grey-900 dark:text-grey-500'
);
const Heading: React.FC<Heading1to5Props | Heading6Props | HeadingLabelProps> = ({
level = 1,

View File

@ -19,9 +19,13 @@ const Link: React.FC<LinkProps> = ({href, color, className, children, ...props})
color = 'green';
}
let styles = (color === 'black') ? `transition text-black hover:text-black-700 ${className}` : `text-${color} hover:text-${color}-400 ${className}`;
let styles = (color === 'black') ? `transition text-black hover:text-black-700` : `text-${color} hover:text-${color}-400`;
if (className) {
styles = `${styles} ${className}`;
}
return <a className={styles} href={href} {...props}>{children}</a>;
};
export default Link;
export default Link;

View File

@ -108,9 +108,9 @@ const ColorPicker: React.FC<{
<div className={containerClassName} onMouseDown={stopPropagation} onTouchStart={stopPropagation}>
<HexColorPicker className='w-full' color={hexValue || '#ffffff'} onChange={onChange} onMouseDown={startUsingColorPicker} onTouchStart={startUsingColorPicker} />
<div className="mt-3 flex gap-2">
<div ref={inputWrapperRef} className='peer relative order-2 flex h-10 w-full items-center border-b border-grey-500 py-2 hover:border-grey-700 focus:border-black' onClick={focusHexInputOnClick}>
<span className='ml-1 mr-2 text-grey-700'>#</span>
<HexColorInput aria-label="Color value" className='z-50 w-full bg-transparent' color={hexValue} onChange={onChange} />
<div ref={inputWrapperRef} className='peer relative order-2 flex h-10 w-full items-center' onClick={focusHexInputOnClick}>
<span className='absolute left-2 top-[9px] z-10 ml-1 mr-2 text-grey-700'>#</span>
<HexColorInput aria-label="Color value" className='z-[1] w-full rounded-md border border-transparent bg-grey-150 p-2 pl-6 transition-all hover:bg-grey-100 focus:border-green focus:bg-white focus:shadow-[0_0_0_1px_rgba(48,207,67,1)] dark:bg-grey-900 dark:text-white dark:focus:bg-grey-925' color={hexValue} onChange={onChange} />
{eyedropper && !!window.EyeDropper && (
<button
className="absolute inset-y-0 right-3 z-50 my-auto h-4 w-4 p-[1px]"

View File

@ -100,7 +100,7 @@ const ColorPickerField = ({testId, title, direction, value, hint, error, eyedrop
if (title) {
content = (
<div className={clsx('flex w-full cursor-pointer items-start first:mt-0', direction === 'rtl' && 'flex-row-reverse')}>
<div className={clsx('flex w-full cursor-pointer items-start first:mt-0 dark:text-white', direction === 'rtl' && 'flex-row-reverse')}>
<div className="shrink-0">
{content}
</div>

View File

@ -26,7 +26,7 @@ const HtmlField: React.FC<HtmlFieldProps> = ({
error,
hint,
value,
clearBg = true,
clearBg = false,
className = '',
containerClassName = '',
hintClassName = '',
@ -34,9 +34,9 @@ const HtmlField: React.FC<HtmlFieldProps> = ({
...props
}) => {
const textFieldClasses = unstyled ? '' : clsx(
'min-h-10 border-b py-2',
clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]',
error ? `border-red` : `border-grey-500 hover:border-grey-700 focus:border-black`,
'flex min-h-[32px] items-center rounded-md border border-transparent py-1.5 md:min-h-[36px]',
clearBg ? 'bg-transparent' : 'bg-grey-150 px-3 dark:bg-grey-900',
error ? `border-red` : `dark:bg-dark-925 hover:bg-grey-100 focus:border-green focus:shadow-[0_0_0_1px_rgba(48,207,67,1)]`,
(title && !hideTitle && !clearBg) && `mt-2`,
className
);

View File

@ -25,13 +25,6 @@ export const Default: Story = {
}
};
export const WithBackground: Story = {
args: {
options: options,
clearBg: false
}
};
export const Black: Story = {
args: {
options: options,

View File

@ -79,7 +79,7 @@ const Option: React.FC<OptionProps<MultiSelectOption, true>> = ({children, ...op
const MultiSelect: React.FC<MultiSelectProps> = ({
title = '',
clearBg = true,
clearBg = false,
error = false,
placeholder,
color = 'grey',
@ -99,12 +99,12 @@ const MultiSelect: React.FC<MultiSelectProps> = ({
const id = useId();
const controlClasses = clsx(
'w-full cursor-pointer appearance-none rounded-md border border-transparent transition-all dark:text-white',
size === 'sm' ? 'min-h-[36px] py-1 text-sm' : 'min-h-[40px] py-2',
'w-full cursor-pointer appearance-none border-b dark:text-white',
fieldStyle === 'dropdown' ? 'cursor-pointer' : 'cursor-text',
!clearBg && 'bg-grey-75 px-[10px] dark:bg-grey-950',
!clearBg && 'bg-grey-150 px-[10px] dark:bg-grey-900',
'outline-none',
error ? 'border-red' : 'border-grey-500 hover:border-grey-700 dark:border-grey-800 dark:hover:border-grey-700',
error ? 'border-red' : 'hover:bg-grey-100 dark:hover:bg-grey-925',
(title && !clearBg) && 'mt-2'
);

View File

@ -69,7 +69,7 @@ export type SelectProps = Props<SelectOption, false> & SelectOptionProps & {
const DropdownIndicator: React.FC<DropdownIndicatorProps<SelectOption, false> & {clearBg: boolean}> = ({clearBg, ...props}) => (
<components.DropdownIndicator {...props}>
<div className={`absolute top-[14px] block h-2 w-2 rotate-45 border-[1px] border-l-0 border-t-0 border-grey-900 content-[''] dark:border-grey-400 ${clearBg ? 'right-0' : 'right-4'} `}></div>
<div className={`absolute top-[11px] block h-2 w-2 rotate-45 border-[1px] border-l-0 border-t-0 border-grey-900 content-[''] dark:border-grey-400 ${clearBg ? 'right-2' : 'right-[14px]'} `}></div>
</components.DropdownIndicator>
);
@ -98,8 +98,7 @@ const Select: React.FC<SelectProps> = ({
onSelect,
error,
hint,
clearBg = true,
border = true,
clearBg = false,
fullWidth = true,
isSearchable = false,
containerClassName,
@ -135,15 +134,14 @@ const Select: React.FC<SelectProps> = ({
const customClasses = {
control: clsx(
controlClasses?.control,
'min-h-[40px] w-full cursor-pointer appearance-none outline-none dark:text-white',
size === 'xs' ? 'py-0 pr-2 text-xs' : 'py-2 pr-4',
border && 'border-b',
!clearBg && 'bg-grey-75 px-[10px] dark:bg-grey-950',
error ? 'border-red' : 'border-grey-500 hover:border-grey-700 dark:border-grey-800 dark:hover:border-grey-700',
(title && !clearBg) && 'mt-2'
'h-9 min-h-[36px] w-full cursor-pointer appearance-none rounded-md border outline-none dark:text-white',
size === 'xs' ? 'py-0 pr-2 text-xs' : 'py-1 pr-4',
clearBg ? '' : 'bg-grey-150 px-3 dark:bg-grey-900',
error ? 'border-red' : `border-transparent ${!clearBg && 'hover:bg-grey-100 dark:hover:bg-grey-925'}`,
(title && !clearBg) && 'mt-1.5'
),
valueContainer: clsx('gap-1', controlClasses?.valueContainer),
placeHolder: clsx('text-grey-500 dark:text-grey-800', controlClasses?.placeHolder),
valueContainer: clsx('mr-1.5 gap-1', controlClasses?.valueContainer),
placeHolder: clsx('text-grey-700 dark:text-grey-800', controlClasses?.placeHolder),
menu: clsx(
'z-[300] rounded-b bg-white shadow dark:border dark:border-grey-900 dark:bg-black',
size === 'xs' && 'text-xs',

View File

@ -46,6 +46,7 @@ export const WithTitle: Story = {
export const WithHint: Story = {
args: {
title: 'Description',
placeholder: 'Enter description',
hint: 'Here\'s some hint'
}

View File

@ -34,7 +34,6 @@ const TextArea: React.FC<TextAreaProps> = ({
error,
placeholder,
hint,
clearBg = true,
fontStyle = 'sans',
className,
onChange,
@ -56,10 +55,9 @@ const TextArea: React.FC<TextAreaProps> = ({
};
let styles = clsx(
'peer order-2 rounded-sm border px-3 py-2 dark:text-white',
clearBg ? 'bg-transparent' : 'bg-grey-75 dark:bg-grey-950',
error ? 'border-red' : 'border-grey-500 placeholder:text-grey-500 hover:border-grey-700 focus:border-grey-800 dark:border-grey-800 dark:placeholder:text-grey-800 dark:hover:border-grey-700 dark:focus:border-grey-500',
title && 'mt-2',
'order-2 rounded-md border bg-grey-150 px-3 py-2 transition-all dark:bg-grey-900 dark:text-white',
error ? 'border-red bg-white' : 'border-transparent placeholder:text-grey-500 hover:bg-grey-100 focus:border-green focus:bg-white focus:shadow-[0_0_0_1px_rgba(48,207,67,1)] dark:placeholder:text-grey-800 dark:hover:bg-grey-925 dark:focus:bg-grey-925',
title && 'mt-1.5',
fontStyle === 'mono' && 'font-mono text-sm',
className
);
@ -97,7 +95,7 @@ const TextArea: React.FC<TextAreaProps> = ({
onFocus={handleFocus}
{...props}>
</textarea>
{title && <Heading className={'order-1 !text-grey-700 peer-focus:!text-black dark:!text-grey-300 dark:peer-focus:!text-white'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{title && <Heading className={'order-1 dark:!text-grey-300'} grey={false} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{hint && <Hint className='order-3' color={error ? 'red' : ''}>{hint}</Hint>}
{maxLength && <Hint>Max length is {maxLength}</Hint>}
</div>

View File

@ -43,13 +43,6 @@ export const Disabled: Story = {
}
};
export const ClearBackground: Story = {
args: {
placeholder: 'Enter something',
clearBg: true
}
};
export const WithValue: Story = {
render: function Component(args) {
const [, updateArgs] = useArgs();
@ -85,15 +78,6 @@ export const WithRightPlaceholder: Story = {
}
};
export const WithoutBorder: Story = {
args: {
title: 'Title',
placeholder: 'Enter something',
hint: 'Here\'s some hint',
border: false
}
};
export const WithDropdown: Story = {
args: {
title: 'Monthly price',
@ -101,6 +85,7 @@ export const WithDropdown: Story = {
rightPlaceholder: (
<Select
border={false}
clearBg={true}
containerClassName='w-14'
fullWidth={false}
options={[
@ -119,7 +104,7 @@ export const WithButton: Story = {
value: 'https://ghost.org',
containerClassName: 'group',
rightPlaceholder: (
<Button className='invisible mt-2 group-hover:visible' color='white' label='Copy' size='sm' />
<Button className='invisible mt-1 rounded-md group-hover:visible' color='white' label='Copy' size='sm' />
)
}
};

View File

@ -38,17 +38,16 @@ const TextField: React.FC<TextFieldProps> = ({
placeholder,
rightPlaceholder,
hint,
clearBg = true,
onChange,
onFocus,
onBlur,
clearBg = false,
className = '',
maxLength,
containerClassName = '',
hintClassName = '',
unstyled = false,
disabled,
border = true,
...props
}) => {
const id = useId();
@ -64,21 +63,31 @@ const TextField: React.FC<TextFieldProps> = ({
setFocusState(false);
};
const disabledBorderClasses = border && 'border-grey-300 dark:border-grey-900';
const enabledBorderClasses = border && 'border-grey-500 hover:border-grey-700 focus:border-black dark:border-grey-800 dark:hover:border-grey-700 dark:focus:border-grey-500';
const fieldContainerClasses = clsx(
'relative order-2 flex w-full items-center',
(title && !hideTitle) && `mt-1.5`
);
const bgClasses = clsx(
'absolute inset-0 rounded-md border text-grey-300 transition-all peer-hover:bg-grey-100 peer-focus:border-green peer-focus:bg-white peer-focus:shadow-[0_0_0_1px_rgba(48,207,67,1)] dark:peer-hover:bg-grey-925 dark:peer-focus:bg-grey-925',
error ? `border-red bg-white dark:bg-grey-925` : 'border-transparent bg-grey-150 dark:bg-grey-900',
disabled && 'bg-grey-100 dark:bg-grey-925'
);
const textFieldClasses = !unstyled && clsx(
'peer order-2 h-8 w-full py-1 text-sm placeholder:text-grey-500 dark:text-white dark:placeholder:text-grey-800 md:h-10 md:py-2 md:text-base',
border && 'border-b',
!border && '-mb-1.5',
clearBg ? 'bg-transparent' : 'bg-grey-75 px-[10px]',
error && border ? `border-red` : `${disabled ? disabledBorderClasses : enabledBorderClasses}`,
(title && !hideTitle && !clearBg) && `mt-2`,
(disabled ? 'cursor-not-allowed text-grey-700 opacity-60 dark:text-grey-800' : ''),
rightPlaceholder && 'w-0 grow',
'peer z-[1] order-2 h-8 w-full bg-transparent px-3 py-1 text-sm placeholder:text-grey-500 dark:placeholder:text-grey-700 md:h-9 md:py-2 md:text-md',
disabled ? 'cursor-not-allowed text-grey-700 opacity-60 dark:text-grey-700' : 'dark:text-white',
rightPlaceholder ? 'w-0 grow rounded-l-md' : 'rounded-md',
className
);
const rightPlaceholderClasses = !unstyled && clsx(
'z-[1] order-3 rounded-r-lg',
(rightPlaceholder ?
((typeof (rightPlaceholder) === 'string') ? 'flex h-8 items-center py-1 pr-3 text-right text-sm text-grey-500 md:h-9 md:text-base' : 'h-9 pr-1')
: 'pr-2')
);
let field = <></>;
const inputField = <input
@ -95,25 +104,13 @@ const TextField: React.FC<TextFieldProps> = ({
onFocus={handleFocus}
{...props} />;
if (rightPlaceholder) {
const rightPHEnabledBorderClasses = 'border-grey-500 dark:border-grey-800 peer-hover:border-grey-700 peer-focus:border-black dark:peer-focus:border-grey-500';
const rightPHClasses = !unstyled && clsx(
'order-3',
border && 'border-b',
!border && '-mb-1.5',
(typeof (rightPlaceholder) === 'string') ? 'h-8 py-1 text-right text-sm text-grey-500 md:h-10 md:py-2 md:text-base' : 'h-10',
error && border ? `border-red` : `${disabled ? disabledBorderClasses : rightPHEnabledBorderClasses}`
);
field = (
<div className='order-2 flex w-full items-center'>
{inputField}
<span className={rightPHClasses || ''}>{rightPlaceholder}</span>
</div>
);
} else {
field = inputField;
}
field = (
<div className={fieldContainerClasses}>
{inputField}
{!clearBg && <div className={bgClasses}></div>}
{rightPlaceholder && <span className={rightPlaceholderClasses || ''}>{rightPlaceholder}</span>}
</div>
);
hintClassName = clsx(
'order-3',
@ -129,7 +126,7 @@ const TextField: React.FC<TextFieldProps> = ({
return (
<div className={containerClassName}>
{field}
{title && <Heading className={hideTitle ? 'sr-only' : 'order-1 !text-grey-700 peer-focus:!text-black dark:!text-grey-300 dark:peer-focus:!text-white'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{title && <Heading className={hideTitle ? 'sr-only' : 'order-1 peer-focus:!text-black dark:!text-grey-300 dark:peer-focus:!text-white'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
{hint && <Hint className={hintClassName} color={error ? 'red' : 'default'}>{hint}</Hint>}
</div>
);

View File

@ -18,6 +18,7 @@ interface SettingGroupProps {
saveState?: SaveState;
customHeader?: React.ReactNode;
customButtons?: React.ReactNode;
beta?: boolean;
children?: React.ReactNode;
hideEditButton?: boolean;
alwaysShowSaveButton?: boolean;
@ -52,6 +53,7 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
saveState,
customHeader,
customButtons,
beta = false,
children,
hideEditButton,
alwaysShowSaveButton = true,
@ -84,9 +86,9 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
if (saveState === 'unsaved') {
styles += ' border-green';
} else if (isEditing){
styles += ' border-grey-300 dark:border-grey-800';
styles += ' border-grey-700 dark:border-grey-600';
} else {
styles += ' border-grey-200 dark:border-grey-900';
styles += ' border-grey-300 dark:border-grey-800 hover:border-grey-500';
}
let viewButtons: ButtonProps[] = [];
@ -166,7 +168,7 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
});
const containerClasses = clsx(
'relative flex-col gap-6 rounded',
'relative flex-col gap-6 rounded-lg transition-all',
border && 'border p-5 md:p-7',
!checkVisible(keywords) ? 'hidden' : 'flex',
(highlight && highlightOnModalClose) && 'before:pointer-events-none before:absolute before:inset-[1px] before:animate-setting-highlight-fade-out before:rounded before:shadow-[0_0_0_3px_rgba(48,207,67,0.45)]',
@ -178,7 +180,7 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
<div className={containerClasses} data-testid={testId}>
<div ref={ref} className='absolute' id={navid && navid}></div>
{customHeader ? customHeader :
<SettingGroupHeader description={description} title={title!}>
<SettingGroupHeader beta={beta} description={description} title={title!}>
{customButtons ? customButtons :
(onEditingChange && <ButtonGroup buttons={isEditing ? editButtons : viewButtons} link linkWithPadding />)}
</SettingGroupHeader>

View File

@ -15,7 +15,7 @@ interface ISettingGroupContent {
}
const SettingGroupContent: React.FC<ISettingGroupContent> = ({columns, values, children, className}) => {
let styles = 'flex flex-col gap-x-6 gap-y-7';
let styles = 'flex flex-col gap-x-5 gap-y-7';
if (columns === 2) {
styles = 'grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6';
}

View File

@ -5,18 +5,19 @@ import {useSearch} from '../../components/providers/ServiceProvider';
interface Props {
title?: string;
description?: React.ReactNode;
beta?: boolean;
children?: React.ReactNode;
}
const SettingGroupHeader: React.FC<Props> = ({title, description, children}) => {
const SettingGroupHeader: React.FC<Props> = ({title, description, children, beta = false}) => {
const {highlightKeywords} = useSearch();
return (
<div className="flex items-start justify-between gap-4">
{(title || description) &&
<div>
<Heading level={5}>{highlightKeywords(title || '')}</Heading>
{description && <p className="mt-0.5 hidden max-w-lg text-sm group-[.is-not-editing]/setting-group:!visible group-[.is-not-editing]/setting-group:!block md:!visible md:!block">{highlightKeywords(description)}</p>}
<Heading level={5}>{highlightKeywords(title || '')}{beta && <sup className='ml-0.5 text-[10px] font-semibold uppercase tracking-wide'>Beta</sup>}</Heading>
{description && <p className="mt-1 hidden max-w-lg group-[.is-not-editing]/setting-group:!visible group-[.is-not-editing]/setting-group:!block md:!visible md:!block">{highlightKeywords(description)}</p>}
</div>
}
<div className='-mt-0.5'>

View File

@ -1,3 +1,4 @@
import Icon from '../global/Icon';
import React from 'react';
import clsx from 'clsx';
import {useScrollSectionContext, useScrollSectionNav} from '../../hooks/useScrollSection';
@ -6,28 +7,33 @@ import {useSearch} from '../../components/providers/ServiceProvider';
interface Props {
title: React.ReactNode;
navid?: string;
icon?: string;
keywords?: string[];
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onClick?: (e:React.MouseEvent<HTMLAnchorElement>) => void;
}
const SettingNavItem: React.FC<Props> = ({
title,
navid = '',
icon,
keywords,
onClick = () => {}
onClick
}) => {
const {ref, props} = useScrollSectionNav(navid);
const {currentSection} = useScrollSectionContext();
const {checkVisible} = useSearch();
const classNames = clsx(
'block px-0 py-1 text-sm dark:text-white',
(currentSection === navid) && 'font-bold',
'w-100 flex h-8 cursor-pointer items-center rounded-md px-2 py-1 text-left text-sm transition-all hover:bg-grey-100 focus:bg-grey-100 dark:text-grey-300 dark:hover:bg-grey-925 dark:focus:bg-grey-900',
(currentSection === navid) && 'bg-grey-200 dark:bg-grey-900',
!checkVisible(keywords || []) && 'hidden'
);
return (
<li ref={ref} {...props}><button className={classNames} name={navid} type='button' onClick={onClick}>{title}</button></li>
<li ref={ref} {...props}><a className={classNames} id={navid} onClick={onClick}>
{icon && <Icon className='mr-[7px]' name={icon} size='sm' />}
{title}
</a></li>
);
};

View File

@ -1,5 +1,4 @@
import React from 'react';
import SettingSectionHeader from './SettingSectionHeader';
import {useSearch} from '../../components/providers/ServiceProvider';
interface Props {
@ -17,9 +16,9 @@ const SettingNavSection: React.FC<Props> = ({title, keywords, children}) => {
return (
<>
{title && <SettingSectionHeader title={title} />}
{title && <h2 className='mb-4 ml-2 text-[16px] tracking-tight'>{title}</h2>}
{children &&
<ul className="mb-10 mt-[-8px]">
<ul className="mb-14 mt-[-8px]">
{children}
</ul>
}

View File

@ -1,5 +1,6 @@
import React from 'react';
import SettingSectionHeader from './SettingSectionHeader';
import clsx from 'clsx';
import {useSearch} from '../../components/providers/ServiceProvider';
interface Props {
@ -11,11 +12,16 @@ interface Props {
const SettingSection: React.FC<Props> = ({title, keywords = [], children}) => {
const {checkVisible} = useSearch();
const containerClassNames = clsx(
'mb-[16vh]',
checkVisible(keywords) ? '' : 'hidden'
);
return (
<div className={checkVisible(keywords) ? '' : 'hidden'}>
{title && <SettingSectionHeader sticky={true} title={title} />}
<div className={containerClassNames}>
{title && <SettingSectionHeader title={title} />}
{children &&
<div className="mb-[100px] flex flex-col gap-9">
<div className="mb-[100px] flex flex-col gap-12">
{children}
</div>
}

View File

@ -1,4 +1,5 @@
import React from 'react';
import clsx from 'clsx';
interface Props {
title: string;
@ -6,12 +7,13 @@ interface Props {
}
const SettingSectionHeader: React.FC<Props> = ({title, sticky = false}) => {
let styles = 'pb-[9px] mb-px text-2xs font-semibold uppercase tracking-wider text-grey-700 z-20 ';
if (sticky) {
styles += ' sticky top-0 -mt-4 pt-4 bg-white dark:bg-black';
}
const classNames = clsx(
'z-20 mb-px pb-10 text-4xl font-bold tracking-tighter',
(sticky ? 'sticky top-0 mt-[calc(-8vmin-4px)] bg-gradient-to-t from-transparent via-white via-20% to-white pt-[calc(8vmin-4px)] dark:bg-black' : 'mt-[-5px]')
);
return (
<h2 className={styles}>{title}</h2>
<h2 className={classNames}>{title}</h2>
);
};

View File

@ -17,7 +17,7 @@ const SettingValue: React.FC<SettingValueProps> = ({heading, value, hint, hideEm
return (
<div className='flex flex-col' {...props}>
{heading && <Heading grey={true} level={6}>{heading}</Heading>}
{heading && <Heading grey={false} level={6}>{heading}</Heading>}
<div className={`flex items-center ${heading && `mt-1`}`}>{value}</div>
{hint && <p className='mt-1 text-xs'>{hint}</p>}
</div>

View File

@ -1,9 +1,11 @@
import Button from '../admin-x-ds/global/Button';
import GhostLogo from '../assets/images/orb-pink.png';
import Icon from '../admin-x-ds/global/Icon';
import React, {useEffect, useRef} from 'react';
import SettingNavItem from '../admin-x-ds/settings/SettingNavItem';
import SettingNavSection from '../admin-x-ds/settings/SettingNavSection';
import TextField from '../admin-x-ds/global/form/TextField';
import clsx from 'clsx';
import useFeatureFlag from '../hooks/useFeatureFlag';
import useRouting from '../hooks/useRouting';
import {searchKeywords as advancedSearchKeywords} from './settings/advanced/AdvancedSettings';
@ -22,7 +24,7 @@ const Sidebar: React.FC = () => {
const searchInputRef = useRef<HTMLInputElement | null>(null);
const {isAnyTextFieldFocused} = useFocusContext();
// Focus in on search field when pressing CMD+K/CTRL+K
// Focus in on search field when pressing "/"
useEffect(() => {
const handleKeyPress = (e: KeyboardEvent) => {
if (e.key === '/' && !isAnyTextFieldFocused) {
@ -48,9 +50,11 @@ const Sidebar: React.FC = () => {
const {settings, config} = useGlobalData();
const [newslettersEnabled] = getSettingValues(settings, ['editor_default_email_recipients']) as [string];
const handleSectionClick = (e: React.MouseEvent<HTMLButtonElement>) => {
setFilter('');
updateRoute(e.currentTarget.name);
const handleSectionClick = (e?: React.MouseEvent<HTMLAnchorElement>) => {
if (e) {
setFilter('');
updateRoute(e.currentTarget.id);
}
};
const hasTipsAndDonations = useFeatureFlag('tipsAndDonations');
@ -64,65 +68,74 @@ const Sidebar: React.FC = () => {
}
};
const navClasses = clsx(
'no-scrollbar hidden pt-10 tablet:!visible tablet:!block tablet:h-[calc(100vh-8vmin-36px)] tablet:overflow-y-auto'
);
return (
<div data-testid="sidebar">
<div className='relative md:pt-4 tablet:h-[64px] tablet:pt-[32px]'>
<Icon className='absolute top-2 md:top-6 tablet:top-10' colorClass='text-grey-500' name='magnifying-glass' size='sm' />
<TextField autoComplete="off" className='border-b border-grey-500 bg-transparent px-3 py-1.5 pl-[24px] text-sm dark:text-white' inputRef={searchInputRef} placeholder="Search" title="Search" value={filter} hideTitle unstyled onChange={updateSearch} />
{filter ? <Button className='absolute -right-1 top-1 p-1 tablet:top-9' icon='close' iconColorClass='text-grey-700 !w-3 !h-3' size='sm' unstyled onClick={() => {
<div className='relative flex content-stretch items-end tablet:h-[36px]'>
<Icon className='absolute left-2 top-[10px] z-10' colorClass='text-grey-500' name='magnifying-glass' size='sm' />
<TextField autoComplete="off" className='-mx-1 flex h-9 w-[calc(100%+8px)] items-center rounded-full border border-transparent bg-grey-150 px-[33px] py-1.5 text-sm transition-all hover:bg-grey-100 focus:border-green focus:bg-white focus:shadow-[0_0_0_1px_rgba(48,207,67,1)] focus:outline-2 dark:bg-grey-900 dark:text-white dark:focus:bg-black' containerClassName='w-100' inputRef={searchInputRef} placeholder="Search settings" title="Search" value={filter} clearBg hideTitle unstyled onChange={updateSearch} />
{filter ? <Button className='absolute right-3 top-[10px] p-1' icon='close' iconColorClass='text-grey-700 !w-[10px] !h-[10px]' size='sm' unstyled onClick={() => {
setFilter('');
}} /> : <div className='absolute right-0 top-[22px] hidden rounded-sm bg-grey-200 px-1 py-0.5 text-2xs font-semibold uppercase tracking-wider text-grey-600 dark:bg-grey-800 dark:text-grey-500 tablet:!visible tablet:top-[38px] tablet:!block'>/</div>}
}} /> : <div className='absolute right-0 top-[20px] hidden rounded border border-grey-400 bg-white px-1.5 py-0.5 text-2xs font-semibold uppercase tracking-wider text-grey-600 shadow-[0px_1px_#CED4D9] dark:bg-grey-800 dark:text-grey-500 tablet:!visible tablet:right-3 tablet:top-[7px] tablet:!block'>/</div>}
</div>
<div className="no-scrollbar hidden pt-10 tablet:!visible tablet:!block tablet:h-[calc(100vh-5vmin-84px-64px)] tablet:w-[240px] tablet:overflow-y-auto" id='admin-x-settings-sidebar'>
<SettingNavSection keywords={Object.values(generalSearchKeywords).flat()} title="General">
<SettingNavItem keywords={generalSearchKeywords.titleAndDescription} navid='general' title="Title & description" onClick={handleSectionClick} />
<SettingNavItem keywords={generalSearchKeywords.timeZone} navid='timezone' title="Timezone" onClick={handleSectionClick} />
<SettingNavItem keywords={generalSearchKeywords.publicationLanguage} navid='publication-language' title="Publication language" onClick={handleSectionClick} />
<SettingNavItem keywords={generalSearchKeywords.metadata} navid='metadata' title="Meta data" onClick={handleSectionClick} />
<SettingNavItem keywords={generalSearchKeywords.twitter} navid='twitter' title="X card" onClick={handleSectionClick} />
<SettingNavItem keywords={generalSearchKeywords.facebook} navid='facebook' title="Facebook card" onClick={handleSectionClick} />
<SettingNavItem keywords={generalSearchKeywords.socialAccounts} navid='social-accounts' title="Social accounts" onClick={handleSectionClick} />
<SettingNavItem keywords={generalSearchKeywords.lockSite} navid='locksite' title="Make this site private" onClick={handleSectionClick} />
<SettingNavItem keywords={generalSearchKeywords.users} navid='staff' title="Staff" onClick={handleSectionClick} />
<div className={navClasses} id='admin-x-settings-sidebar'>
<SettingNavSection keywords={Object.values(generalSearchKeywords).flat()} title="General settings">
<SettingNavItem icon='textfield' keywords={generalSearchKeywords.titleAndDescription} navid='general' title="Title & description" onClick={handleSectionClick} />
<SettingNavItem icon='world-clock' keywords={generalSearchKeywords.timeZone} navid='timezone' title="Timezone" onClick={handleSectionClick} />
<SettingNavItem icon='language' keywords={generalSearchKeywords.publicationLanguage} navid='publication-language' title="Publication language" onClick={handleSectionClick} />
<SettingNavItem icon='layer' keywords={generalSearchKeywords.metadata} navid='metadata' title="Meta data" onClick={handleSectionClick} />
<SettingNavItem icon='twitter-x' keywords={generalSearchKeywords.twitter} navid='twitter' title="X card" onClick={handleSectionClick} />
<SettingNavItem icon='facebook' keywords={generalSearchKeywords.facebook} navid='facebook' title="Facebook card" onClick={handleSectionClick} />
<SettingNavItem icon='like' keywords={generalSearchKeywords.socialAccounts} navid='social-accounts' title="Social accounts" onClick={handleSectionClick} />
<SettingNavItem icon='lock-locked' keywords={generalSearchKeywords.lockSite} navid='locksite' title="Make this site private" onClick={handleSectionClick} />
<SettingNavItem icon='user-page' keywords={generalSearchKeywords.users} navid='staff' title="Staff" onClick={handleSectionClick} />
</SettingNavSection>
<SettingNavSection keywords={Object.values(siteSearchKeywords).flat()} title="Site">
<SettingNavItem keywords={siteSearchKeywords.design} navid='design' title="Design & branding" onClick={handleSectionClick} />
<SettingNavItem keywords={siteSearchKeywords.navigation} navid='navigation' title="Navigation" onClick={handleSectionClick} />
<SettingNavItem keywords={siteSearchKeywords.announcementBar} navid='announcement-bar' title="Announcement bar" onClick={handleSectionClick} />
<SettingNavItem icon='palette' keywords={siteSearchKeywords.design} navid='design' title="Design & branding" onClick={handleSectionClick} />
<SettingNavItem icon='navigation' keywords={siteSearchKeywords.navigation} navid='navigation' title="Navigation" onClick={handleSectionClick} />
<SettingNavItem icon='megaphone' keywords={siteSearchKeywords.announcementBar} navid='announcement-bar' title="Announcement bar" onClick={handleSectionClick} />
</SettingNavSection>
<SettingNavSection keywords={Object.values(membershipSearchKeywords).flat()} title="Membership">
<SettingNavItem keywords={membershipSearchKeywords.access} navid='members' title="Access" onClick={handleSectionClick} />
<SettingNavItem keywords={membershipSearchKeywords.portal} navid='portal' title="Portal" onClick={handleSectionClick} />
<SettingNavItem keywords={membershipSearchKeywords.tiers} navid='tiers' title="Tiers" onClick={handleSectionClick} />
{hasTipsAndDonations && <SettingNavItem keywords={membershipSearchKeywords.tips} navid='tips-or-donations' title="Tips or donations" onClick={handleSectionClick} />}
<SettingNavItem keywords={membershipSearchKeywords.embedSignupForm} navid='embed-signup-form' title="Embeddable signup form" onClick={handleSectionClick} />
{hasRecommendations && <SettingNavItem keywords={membershipSearchKeywords.recommendations} navid='recommendations' title="Recommendations" onClick={handleSectionClick} />}
<SettingNavItem keywords={membershipSearchKeywords.analytics} navid='analytics' title="Analytics" onClick={handleSectionClick} />
<SettingNavItem icon='key' keywords={membershipSearchKeywords.access} navid='members' title="Access" onClick={handleSectionClick} />
<SettingNavItem icon='portal' keywords={membershipSearchKeywords.portal} navid='portal' title="Portal" onClick={handleSectionClick} />
<SettingNavItem icon='bills' keywords={membershipSearchKeywords.tiers} navid='tiers' title="Tiers" onClick={handleSectionClick} />
{hasTipsAndDonations && <SettingNavItem icon='piggybank' keywords={membershipSearchKeywords.tips} navid='tips-or-donations' title="Tips or donations" onClick={handleSectionClick} />}
<SettingNavItem icon='emailfield' keywords={membershipSearchKeywords.embedSignupForm} navid='embed-signup-form' title="Embeddable signup form" onClick={handleSectionClick} />
{hasRecommendations && <SettingNavItem icon='heart' keywords={membershipSearchKeywords.recommendations} navid='recommendations' title="Recommendations" onClick={handleSectionClick} />}
<SettingNavItem icon='baseline-chart' keywords={membershipSearchKeywords.analytics} navid='analytics' title="Analytics" onClick={handleSectionClick} />
</SettingNavSection>
<SettingNavSection keywords={Object.values(emailSearchKeywords).flat()} title="Email newsletter">
<SettingNavItem keywords={emailSearchKeywords.enableNewsletters} navid='enable-newsletters' title="Newsletter sending" onClick={handleSectionClick} />
<SettingNavItem icon='email-check' keywords={emailSearchKeywords.enableNewsletters} navid='enable-newsletters' title="Newsletter sending" onClick={handleSectionClick} />
{newslettersEnabled !== 'disabled' && (
<>
<SettingNavItem keywords={emailSearchKeywords.defaultRecipients} navid='default-recipients' title="Default recipients" onClick={handleSectionClick} />
<SettingNavItem keywords={emailSearchKeywords.newsletters} navid='newsletters' title="Newsletters" onClick={handleSectionClick} />
{!config.mailgunIsConfigured && <SettingNavItem keywords={emailSearchKeywords.mailgun} navid='mailgun' title="Mailgun settings" onClick={handleSectionClick} />}
<SettingNavItem icon='recepients' keywords={emailSearchKeywords.defaultRecipients} navid='default-recipients' title="Default recipients" onClick={handleSectionClick} />
<SettingNavItem icon='email' keywords={emailSearchKeywords.newsletters} navid='newsletters' title="Newsletters" onClick={handleSectionClick} />
{!config.mailgunIsConfigured && <SettingNavItem icon='at-sign' keywords={emailSearchKeywords.mailgun} navid='mailgun' title="Mailgun settings" onClick={handleSectionClick} />}
</>
)}
</SettingNavSection>
<SettingNavSection keywords={Object.values(advancedSearchKeywords).flat()} title="Advanced">
<SettingNavItem keywords={advancedSearchKeywords.integrations} navid='integrations' title="Integrations" onClick={handleSectionClick} />
<SettingNavItem keywords={advancedSearchKeywords.codeInjection} navid='code-injection' title="Code injection" onClick={handleSectionClick} />
<SettingNavItem keywords={advancedSearchKeywords.labs} navid='labs' title="Labs" onClick={handleSectionClick} />
<SettingNavItem keywords={advancedSearchKeywords.history} navid='history' title="History" onClick={handleSectionClick} />
<SettingNavItem icon='modules-3' keywords={advancedSearchKeywords.integrations} navid='integrations' title="Integrations" onClick={handleSectionClick} />
<SettingNavItem icon='brackets' keywords={advancedSearchKeywords.codeInjection} navid='code-injection' title="Code injection" onClick={handleSectionClick} />
<SettingNavItem icon='labs-flask' keywords={advancedSearchKeywords.labs} navid='labs' title="Labs" onClick={handleSectionClick} />
<SettingNavItem icon='time-back' keywords={advancedSearchKeywords.history} navid='history' title="History" onClick={handleSectionClick} />
</SettingNavSection>
<Button className='mb-10 !font-normal' label='About Ghost' link onClick={() => {
{!filter &&
<a className='mb-10 ml-1 flex cursor-pointer items-center gap-1.5 pl-1 text-sm !font-normal' onClick={() => {
updateRoute('about');
}} />
}}>
<img alt='Ghost Logo' className='h-[18px] w-[18px]' src={GhostLogo} />
About Ghost
</a>
}
</div>
</div>
);

View File

@ -63,6 +63,7 @@ const AddIntegrationModal: React.FC<RoutingModalProps> = () => {
marginTop={false}
>
<TextField
autoFocus={true}
error={!!errors.name}
hint={errors.name}
placeholder='Custom integration'

View File

@ -17,7 +17,7 @@ const features = [{
flag: 'webmentions'
},{
title: 'Websockets',
description: 'Test out Websockets functionality at <code>/ghost/#/websockets</code>.',
description: <>Test out Websockets functionality at <code>/ghost/#/websockets</code>.</>,
flag: 'websockets'
},{
title: 'Stripe Automatic Tax',
@ -48,9 +48,13 @@ const features = [{
description: 'Enables publishers to collect one-time payments',
flag: 'tipsAndDonations'
},{
title: 'Recommendations',
description: 'Enables publishers to recommend sites to their audience',
flag: 'recommendations'
title: 'List-Unsubscribe header',
description: 'Set the List-Unsubscribe header in emails',
flag: 'listUnsubscribeHeader'
},{
title: 'Editor emoji picker',
description: <>Trigger an emoji picker when typing <code>{':{search}'}</code> in the editor</>,
flag: 'editorEmojiPicker'
}];
const AlphaFeatures: React.FC = () => {

View File

@ -26,7 +26,7 @@ const AddNewsletterModal: React.FC<RoutingModalProps> = () => {
});
const {mutateAsync: addNewsletter} = useAddNewsletter();
const {formState, updateForm, handleSave, errors, validate, clearError} = useForm({
const {formState, updateForm, handleSave, errors, clearError} = useForm({
initialState: {
name: '',
description: '',
@ -96,12 +96,12 @@ const AddNewsletterModal: React.FC<RoutingModalProps> = () => {
marginTop
>
<TextField
autoFocus={true}
error={Boolean(errors.name)}
hint={errors.name}
placeholder='Weekly roundup'
title='Name'
value={formState.name}
onBlur={validate}
onChange={e => updateForm(state => ({...state, name: e.target.value}))}
onKeyDown={() => clearError('name')}
/>

View File

@ -105,7 +105,6 @@ const Facebook: React.FC<{ keywords: string[] }> = ({keywords}) => {
</ImageUpload>
<div className="flex flex-col gap-x-6 gap-y-7 px-4 pb-7">
<TextField
clearBg={true}
inputRef={focusRef}
placeholder={siteTitle}
title="Facebook title"
@ -113,7 +112,6 @@ const Facebook: React.FC<{ keywords: string[] }> = ({keywords}) => {
onChange={handleTitleChange}
/>
<TextField
clearBg={true}
placeholder={siteDescription}
title="Facebook description"
value={facebookDescription}

View File

@ -19,13 +19,13 @@ export const searchKeywords = {
twitter: ['twitter card', 'structured data', 'rich cards', 'x card'],
facebook: ['facebook card', 'structured data', 'rich cards'],
socialAccounts: ['social accounts', 'facebook', 'twitter', 'structured data', 'rich cards'],
lockSite: ['password', 'lock site', 'make this site private'],
users: ['users and permissions', 'roles', 'staff']
lockSite: ['password protection', 'lock site', 'make this site private'],
users: ['users and permissions', 'roles', 'staff', 'invite people', 'contributors', 'editors', 'authors', 'administrators']
};
const GeneralSettings: React.FC = () => {
return (
<SettingSection keywords={Object.values(searchKeywords).flat()} title="General">
<SettingSection keywords={Object.values(searchKeywords).flat()} title="General settings">
<TitleAndDescription keywords={searchKeywords.titleAndDescription} />
<TimeZone keywords={searchKeywords.timeZone} />
<PublicationLanguage keywords={searchKeywords.publicationLanguage} />

View File

@ -189,7 +189,6 @@ const InviteUserModal = NiceModal.create(() => {
Send an invitation for a new person to create a staff account on your site, and select a role that matches what youd like them to be able to do.
</p>
<TextField
clearBg={true}
error={!!errors.email}
hint={errors.email}
inputRef={focusRef}

View File

@ -68,7 +68,7 @@ const TimeZone: React.FC<{ keywords: string[] }> = ({keywords}) => {
key: 'site-timezone',
value: <div className='flex flex-col'>
{publicationTimezoneData?.label || publicationTimezone}
<span className='text-sm'><Hint timezone={publicationTimezone} /></span>
<span className='text-xs'><Hint timezone={publicationTimezone} /></span>
</div>
}
]} />

View File

@ -102,7 +102,6 @@ const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => {
</ImageUpload>
<div className="flex flex-col gap-x-6 gap-y-7 px-4 pb-7">
<TextField
clearBg={true}
inputRef={focusRef}
placeholder={siteTitle}
title="X title"
@ -110,7 +109,6 @@ const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => {
onChange={handleTitleChange}
/>
<TextField
clearBg={true}
placeholder={siteDescription}
title="X description"
value={twitterDescription}

View File

@ -11,7 +11,7 @@ import useFeatureFlag from '../../../hooks/useFeatureFlag';
export const searchKeywords = {
portal: ['portal', 'signup', 'sign up', 'signin', 'sign in', 'login', 'account', 'membership'],
access: ['default', 'access', 'subscription', 'post', 'membership'],
access: ['default', 'access', 'subscription', 'post', 'membership', 'comments', 'commenting'],
tiers: ['tiers', 'payment', 'paid', 'stripe'],
tips: ['tip', 'donation', 'one time', 'payment'],
embedSignupForm: ['embeddable signup form', 'embeddable form', 'embeddable sign up form', 'embeddable sign up'],

View File

@ -122,6 +122,7 @@ const Recommendations: React.FC<{ keywords: string[] }> = ({keywords}) => {
return (
<SettingGroup
beta={true}
customButtons={buttons}
description={groupDescription}
keywords={keywords}

View File

@ -77,7 +77,7 @@ const TipsOrDonations: React.FC<{ keywords: string[] }> = ({keywords}) => {
<div className='w-100'>
<div className='flex items-center gap-2'>
<Heading level={6}>Shareable link &mdash;</Heading>
<button className='text-2xs font-semibold uppercase tracking-wider text-green' type="button" onClick={openPreview}>Preview</button>
<button className='text-xs tracking-wide text-green' type="button" onClick={openPreview}>Preview</button>
</div>
<div className='w-100 group relative -m-1 mt-0 overflow-hidden rounded p-1 hover:bg-grey-50 dark:hover:bg-grey-900'>
{donateUrl}

View File

@ -220,14 +220,15 @@ const TierDetailModalContent: React.FC<{tier?: Tier}> = ({tier}) => {
<div className='basis-1/2'>
<div className='mb-1 flex h-6 items-center justify-between'>
<Heading level={6}>Prices</Heading>
<div className='w-10'>
<div className='-mr-2 w-[50px]'>
<Select
border={false}
containerClassName='font-medium'
controlClasses={{menu: 'w-14'}}
controlClasses={{menu: 'w-18'}}
options={currencySelectGroups()}
selectedOption={currencySelectGroups().flatMap(group => group.options).find(option => option.value === formState.currency)}
size='xs'
clearBg
isSearchable
onSelect={option => updateForm(state => ({...state, currency: option?.value}))}
/>
@ -297,17 +298,16 @@ const TierDetailModalContent: React.FC<{tier?: Tier}> = ({tier}) => {
renderItem={({id, item}) => <div className='relative flex w-full items-center gap-5'>
<div className='absolute left-[-32px] top-[7px] flex h-6 w-6 items-center justify-center bg-white group-hover:hidden'><Icon name='check' size='sm' /></div>
<TextField
className='grow border-b border-grey-500 py-2 focus:border-grey-800 group-hover:border-grey-600'
// className='grow border-b border-grey-500 py-2 focus:border-grey-800 group-hover:border-grey-600'
value={item}
unstyled
onChange={e => benefits.updateItem(id, e.target.value)}
/>
<Button className='absolute right-0 top-1' icon='trash' iconColorClass='opacity-0 group-hover:opacity-100' size='sm' onClick={() => benefits.removeItem(id)} />
<Button className='absolute right-1 top-1 z-10' icon='trash' iconColorClass='opacity-0 group-hover:opacity-100' size='sm' onClick={() => benefits.removeItem(id)} />
</div>}
onMove={benefits.moveItem}
/>
</div>
<div className="relative mt-0.5 flex items-center gap-3">
<div className="relative mt-1 flex items-center gap-3">
<Icon className='dark:text-white' name='check' size='sm' />
<TextField
className='grow'
@ -324,7 +324,7 @@ const TierDetailModalContent: React.FC<{tier?: Tier}> = ({tier}) => {
}}
/>
<Button
className='absolute right-0 top-1'
className='absolute right-1 top-1 z-10'
color='green'
icon='add'
iconColorClass='text-white'

View File

@ -58,7 +58,7 @@ const NavigationModal = NiceModal.create(() => {
}
}}
>
<div className='mt-6'>
<div className='mb-1 mt-6'>
<TabView
selectedTab={selectedTab}
tabs={[

View File

@ -51,7 +51,6 @@ const BrandSettings: React.FC<{ values: BrandSettingValues, updateSetting: (key:
<SettingGroupContent>
<TextField
key='site-description'
clearBg={true}
hint='Used in your theme, meta data and search results'
title='Site description'
value={siteDescription}

View File

@ -76,7 +76,7 @@ const ThemeSetting: React.FC<{
<Heading useLabelTag>{humanizeSettingKey(setting.key)}</Heading>
<ImageUpload
height={setting.value ? '100px' : '32px'}
id='cover-image'
id={`custom-${setting.key}`}
imageURL={setting.value || ''}
onDelete={() => setSetting(null)}
onUpload={file => handleImageUpload(file)}

View File

@ -15,7 +15,7 @@ const NavigationEditForm: React.FC<{
itemSeparator={false}
renderItem={item => (
<NavigationItemEditor
action={<Button className='self-center' icon="trash" iconColorClass='dark:text-white' size='sm' onClick={() => navigation.removeItem(item.id)} />}
action={<Button className='mt-1 self-center' icon="trash" iconColorClass='dark:text-white' size='sm' onClick={() => navigation.removeItem(item.id)} />}
baseUrl={baseUrl}
clearError={key => navigation.clearError(item.id, key)}
item={item}
@ -27,7 +27,7 @@ const NavigationEditForm: React.FC<{
<div className='flex items-center gap-3'>
<Icon colorClass='text-grey-300 dark:text-grey-900 mt-1' name='add' size='sm' />
<NavigationItemEditor
action={<Button className='mx-2 self-center rounded bg-green p-1' data-testid="add-button" icon="add" iconColorClass='text-white' size='sm' unstyled onClick={navigation.addItem} />}
action={<Button className='mx-2 mt-1 self-center rounded bg-green p-1' data-testid="add-button" icon="add" iconColorClass='text-white' size='sm' unstyled onClick={navigation.addItem} />}
baseUrl={baseUrl}
className="mt-1"
clearError={key => navigation.clearError(navigation.newItem.id, key)}

View File

@ -126,6 +126,7 @@ const ThemePreview: React.FC<{
fullWidth={false}
options={variantOptions}
selectedOption={selectedVariant}
clearBg
onSelect={(option) => {
setSelectedVariant(option || undefined);
}}

View File

@ -17,7 +17,7 @@
@apply font-sans text-black text-base leading-normal;
}
h1, h2, h3, h4, h5, h6 {
h1, h2, h3, h4, h5 {
@apply font-bold tracking-tight leading-tighter;
}
@ -57,6 +57,17 @@
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
letter-spacing: unset;
height: 100vh;
width: 100%;
overflow-x: hidden;
overflow-y: auto;
}
@media (max-width: 860px) {
.admin-x-base {
height: calc(100vh - 55px);
}
}
.admin-x-base.dark {

View File

@ -25,7 +25,8 @@ export function resolveAsset(assetPath: string, relativeTo: string) {
export function getLocalTime(timeZone: string) {
const date = new Date();
const options = {timeZone: timeZone};
const localTime = date.toLocaleString('en-US', options);
const userLocale = navigator.language.startsWith('en') ? navigator.language : 'en-US';
const localTime = date.toLocaleString(userLocale, options);
return localTime;
}

View File

@ -24,6 +24,7 @@ module.exports = {
50: '#FAFAFB',
75: '#F9FAFB',
100: '#F4F5F6',
150: '#F1F3F4',
200: '#EBEEF0',
300: '#DDE1E5',
400: '#CED4D9',
@ -32,6 +33,7 @@ module.exports = {
700: '#7C8B9A',
800: '#626D79',
900: '#394047',
925: '#2E3338',
950: '#222427'
},
green: {
@ -265,10 +267,10 @@ module.exports = {
},
fontSize: {
'2xs': '1.0rem',
base: '1.5rem',
base: '1.45rem',
xs: '1.2rem',
sm: '1.35rem',
md: '1.5rem',
md: '1.45rem',
lg: '1.75rem',
xl: '2rem',
'2xl': '2.4rem',

View File

@ -51,14 +51,12 @@ export const responseFixtures = {
};
let defaultLabFlags = {
recommendations: false,
audienceFeedback: false,
collections: false,
themeErrorsNotification: false,
outboundLinkTagging: false,
announcementBar: false,
signupForm: false,
lexicalEditor: false,
members: false
};
@ -241,6 +239,6 @@ export async function testUrlValidation(input: Locator, textToEnter: string, exp
expect(input).toHaveValue(expectedResult);
if (expectedError) {
await expect(input.locator('xpath=..')).toContainText(expectedError);
await expect(input.locator('xpath=../..')).toContainText(expectedError);
}
};

View File

@ -1,6 +1,6 @@
{
"name": "@tryghost/announcement-bar",
"version": "1.1.7",
"version": "1.1.8",
"license": "MIT",
"repository": {
"type": "git",
@ -36,28 +36,28 @@
},
"eslintConfig": {
"env": {
"browser": true,
"jest": true
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2022
},
"extends": [
"plugin:ghost/browser",
"plugin:react/recommended"
],
"plugins": [
"ghost"
],
"rules": {
"react/prop-types": "off"
},
"settings": {
"react": {
"version": "detect"
}
"browser": true,
"jest": true
},
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2022
},
"extends": [
"plugin:ghost/browser",
"plugin:react/recommended"
],
"plugins": [
"ghost"
],
"rules": {
"react/prop-types": "off"
},
"settings": {
"react": {
"version": "detect"
}
}
},
"browserslist": {
"production": [
@ -80,7 +80,7 @@
},
"devDependencies": {
"@vitejs/plugin-react": "4.1.0",
"vite": "4.4.11",
"vite": "4.5.0",
"vite-plugin-svgr": "3.3.0",
"vitest": "0.34.3"
}

View File

@ -75,8 +75,8 @@
"eslint-plugin-tailwindcss": "3.13.0",
"jsdom": "22.1.0",
"postcss": "8.4.31",
"tailwindcss": "3.3.3",
"vite": "4.4.11",
"tailwindcss": "3.3.5",
"vite": "4.5.0",
"vite-plugin-css-injected-by-js": "3.3.0",
"vite-plugin-svgr": "3.3.0",
"vitest": "0.34.3"

View File

@ -94,7 +94,7 @@
"jsdom": "22.1.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"vite": "4.4.11",
"vite": "4.5.0",
"vite-plugin-css-injected-by-js": "3.3.0",
"vite-plugin-svgr": "3.3.0",
"vitest": "0.34.3"

View File

@ -63,8 +63,8 @@
"rollup-plugin-node-builtins": "2.1.2",
"storybook": "7.4.0",
"stylelint": "15.10.3",
"tailwindcss": "3.3.3",
"vite": "4.4.11",
"tailwindcss": "3.3.5",
"vite": "4.5.0",
"vite-plugin-commonjs": "0.10.0",
"vite-plugin-svgr": "3.3.0",
"vitest": "0.34.3",

View File

@ -86,7 +86,7 @@
"@testing-library/react": "12.1.5",
"@vitejs/plugin-react": "4.1.0",
"nock": "13.3.3",
"vite": "4.4.11",
"vite": "4.5.0",
"vite-plugin-svgr": "3.3.0",
"vitest": "0.34.3"
}

View File

@ -84,7 +84,7 @@ export default class KoenigLexicalEditorInput extends Component {
onFocus={props.onFocus}
isSnippetsEnabled={false}
singleParagraph={true}
className="koenig-lexical-editor-input"
className={`koenig-lexical-editor-input ${this.feature.nightShift ? 'dark' : ''}`}
placeholderText={props.placeholderText}
placeholderClassName="koenig-lexical-editor-input-placeholder"
>

View File

@ -292,7 +292,8 @@ export default class KoenigLexicalEditor extends Component {
fetchLabels,
feature: {
collectionsCard: this.feature.get('collectionsCard'),
collections: this.feature.get('collections')
collections: this.feature.get('collections'),
emojiPicker: this.feature.get('editorEmojiPicker')
},
depreciated: {
headerV1: true // if false, shows header v1 in the menu

View File

@ -3,9 +3,11 @@ import React from 'react';
import ReactDOM from 'react-dom';
import Route from '@ember/routing/route';
import ShortcutsRoute from 'ghost-admin/mixins/shortcuts-route';
import appConfig from 'ghost-admin/config/environment';
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd';
import windowProxy from 'ghost-admin/utils/window-proxy';
import {InitSentryForEmber} from '@sentry/ember';
import {RewriteFrames} from '@sentry/integrations';
import {importSettings} from '../components/admin-x/settings';
import {inject} from 'ghost-admin/decorators/inject';
import {
@ -179,6 +181,18 @@ export default Route.extend(ShortcutsRoute, {
event.tags.grammarly = !!document.querySelector('[data-gr-ext-installed]');
return event;
},
integrations: [
new RewriteFrames({
iteratee: (frame) => {
// Remove duplicate `assets/` from CDN file paths (unsure why this occurs though)
if (frame.filename?.startsWith(appConfig.cdnUrl) && frame.filename?.includes('assets/assets/')) {
frame.filename = frame.filename.replace('assets/assets/', 'assets/');
}
return frame;
}
})
],
// TransitionAborted errors surface from normal application behaviour
// - https://github.com/emberjs/ember.js/issues/12505
ignoreErrors: [/^TransitionAborted$/]

View File

@ -77,6 +77,7 @@ export default class FeatureService extends Service {
@feature('tipsAndDonations') tipsAndDonations;
@feature('recommendations') recommendations;
@feature('lexicalIndicators') lexicalIndicators;
@feature('editorEmojiPicker') editorEmojiPicker;
_user = null;

View File

@ -60,7 +60,9 @@ export default class SessionService extends ESASessionService {
resolve({
...event,
release: `ghost@${this.config.version}`,
'user.role': this.user.role.name
user: {
role: this.user.role.name
}
});
});
});

View File

@ -351,20 +351,6 @@
</div>
</div>
</div>
<div class="gh-expandable-block">
<div class="gh-expandable-header">
<div>
<h4 class="gh-expandable-title">Recommendations</h4>
<p class="gh-expandable-description">
Enables publishers to recommend sites to their audience
</p>
</div>
<div class="for-switch">
<GhFeatureFlag @flag="recommendations" />
</div>
</div>
</div>
</div>
</div>
{{/if}}

View File

@ -1,6 +1,6 @@
{
"name": "ghost-admin",
"version": "5.70.1",
"version": "5.70.2",
"description": "Ember.js admin client for Ghost",
"author": "Ghost Foundation",
"homepage": "http://ghost.org",
@ -26,7 +26,7 @@
"lint": "yarn lint:js && yarn lint:hbs"
},
"engines": {
"node": "^16.14.0 || ^18.12.1"
"node": "^18.12.1"
},
"devDependencies": {
"@babel/eslint-parser": "7.22.15",
@ -40,6 +40,7 @@
"@glimmer/component": "1.1.2",
"@html-next/vertical-collection": "3.0.0",
"@sentry/ember": "7.70.0",
"@sentry/integrations": "7.75.1",
"@tryghost/color-utils": "0.1.24",
"@tryghost/ember-promise-modals": "2.0.1",
"@tryghost/helpers": "1.1.77",
@ -112,7 +113,7 @@
"ember-simple-auth": "5.0.0",
"ember-sinon": "5.0.0",
"ember-source": "3.24.0",
"ember-svg-jar": "2.4.6",
"ember-svg-jar": "2.4.7",
"ember-template-lint": "5.11.0",
"ember-test-selectors": "6.0.0",
"ember-tooltips": "3.6.0",
@ -198,4 +199,4 @@
}
}
}
}
}

View File

@ -46,7 +46,7 @@ function finaliseStructuredData(meta) {
function getMembersHelper(data, frontendKey) {
// Do not load Portal if both Memberships and Tips & Donations are disabled
if (!settingsCache.get('members_enabled') && !settingsCache.get('donations_enabled')) {
if (!settingsCache.get('members_enabled') && !(settingsCache.get('donations_enabled') && labs.isSet('tipsAndDonations'))) {
return '';
}

View File

@ -3,7 +3,11 @@
{{#each recommendations as |rec|}}
<li class="recommendation">
<a href="{{rec.url}}" data-recommendation="{{rec.id}}" target="_blank" rel="noopener">
<img class="recommendation-favicon" src="{{rec.favicon}}" alt="{{rec.title}}" loading="lazy">
<div class="recommendation-favicon">
{{#if rec.favicon}}
<img src="{{rec.favicon}}" alt="{{rec.title}}" loading="lazy" onerror="this.style.display='none';">
{{/if}}
</div>
<h5 class="recommendation-title">{{rec.title}}</h5>
<span class="recommendation-url">{{readable_url rec.url}}</span>
<p class="recommendation-description">{{rec.description}}</p>

View File

@ -1,7 +1,9 @@
const debug = require('@tryghost/debug')('services:routing:controllers:unsubscribe');
const url = require('url');
const members = require('../../../../server/services/members');
const urlUtils = require('../../../../shared/url-utils');
const labs = require('../../../../shared/labs');
const logging = require('@tryghost/logging');
module.exports = async function unsubscribeController(req, res) {
debug('unsubscribeController');
@ -13,6 +15,44 @@ module.exports = async function unsubscribeController(req, res) {
return res.end('Email address not found.');
}
if (req.method === 'POST' && labs.isSet('listUnsubscribeHeader')) {
logging.info('[List-Unsubscribe] Received POST unsubscribe for ' + query.uuid + ', newsletter: ' + (query.newsletter ?? 'null') + ', comments: ' + (query.comments ?? 'false'));
// Do an actual unsubscribe
try {
const member = await members.api.members.get({uuid: query.uuid}, {withRelated: ['newsletters']});
if (member) {
if (query.comments) {
// Unsubscribe from comments
await members.api.members.update({
enable_comment_notifications: false
}, {
id: member.id
});
} else {
const filteredNewsletters = query.newsletter ?
member.related('newsletters').models
.filter(n => n.get('uuid') !== query.newsletter)
.map(n => ({id: n.id}))
: [];
await members.api.members.update({
newsletters: filteredNewsletters
}, {
id: member.id
});
}
}
} catch (e) {
logging.error({
err: e,
message: '[List-Unsubscribe] Failed POST unsubscribe for ' + query.uuid
});
return res.status(400).end();
}
return res.status(201).end();
}
const redirectUrl = new URL(urlUtils.urlFor('home', true));
redirectUrl.searchParams.append('uuid', query.uuid);
if (query.newsletter) {

View File

@ -6,7 +6,6 @@ const slugFilterOrder = require('./utils/slug-filter-order');
const localUtils = require('../../index');
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
const clean = require('./utils/clean');
const labs = require('../../../../../../shared/labs');
const lexical = require('../../../../../lib/lexical');
function removeSourceFormats(frame) {
@ -57,7 +56,7 @@ function defaultFormat(frame) {
return;
}
frame.options.formats = 'mobiledoc';
frame.options.formats = 'mobiledoc,lexical';
}
function handlePostsMeta(frame) {
@ -138,9 +137,7 @@ module.exports = {
// normally we don't allow both mobiledoc+lexical but the model layer will remove lexical
// if mobiledoc is already present to avoid migrating formats outside of an explicit conversion
if (labs.isSet('lexicalEditor')) {
frame.data.pages[0].lexical = JSON.stringify(lexical.htmlToLexicalConverter(html));
}
frame.data.pages[0].lexical = JSON.stringify(lexical.htmlToLexicalConverter(html));
}
}

View File

@ -6,7 +6,6 @@ const localUtils = require('../../index');
const mobiledoc = require('../../../../../lib/mobiledoc');
const postsMetaSchema = require('../../../../../data/schema').tables.posts_meta;
const clean = require('./utils/clean');
const labs = require('../../../../../../shared/labs');
const lexical = require('../../../../../lib/lexical');
function removeSourceFormats(frame) {
@ -74,7 +73,7 @@ function defaultFormat(frame) {
return;
}
frame.options.formats = 'mobiledoc';
frame.options.formats = 'mobiledoc,lexical';
}
function handlePostsMeta(frame) {
@ -172,9 +171,7 @@ module.exports = {
// normally we don't allow both mobiledoc+lexical but the model layer will remove lexical
// if mobiledoc is already present to avoid migrating formats outside of an explicit conversion
if (labs.isSet('lexicalEditor')) {
frame.data.posts[0].lexical = JSON.stringify(lexical.htmlToLexicalConverter(html));
}
frame.data.posts[0].lexical = JSON.stringify(lexical.htmlToLexicalConverter(html));
}
}

View File

@ -16,6 +16,28 @@ function populateNodes() {
}
module.exports = {
get blankDocument() {
return {
root: {
children: [
{
children: [],
direction: null,
format: '',
indent: 0,
type: 'paragraph',
version: 1
}
],
direction: null,
format: '',
indent: 0,
type: 'root',
version: 1
}
};
},
get lexicalHtmlRenderer() {
if (!lexicalHtmlRenderer) {
if (!nodes) {

View File

@ -392,6 +392,11 @@ Post = ghostBookshelf.Model.extend({
const html = await lexicalLib.render(model.get('lexical'));
const plaintext = htmlToPlaintext.excerpt(html);
// avoid a DB query if we have no html - knex will set it to an empty string rather than NULL
if (!html && !model.get('plaintext')) {
return model;
}
// set model attributes so they are available immediately in code that uses the returned model
model.set('html', html);
model.set('plaintext', plaintext);
@ -716,7 +721,7 @@ Post = ghostBookshelf.Model.extend({
}
if (!this.get('mobiledoc') && !this.get('lexical')) {
this.set('mobiledoc', JSON.stringify(mobiledocLib.blankDocument));
this.set('lexical', JSON.stringify(lexicalLib.blankDocument));
}
// If we're force re-rendering we want to make sure that all image cards
@ -984,7 +989,7 @@ Post = ghostBookshelf.Model.extend({
}
// CASE: Convert post to lexical on the fly
if (labs.isSet('lexicalEditor') && options.convert_to_lexical) {
if (options.convert_to_lexical) {
ops.push(async function convertToLexical() {
const mobiledoc = model.get('mobiledoc');
if (mobiledoc !== null) { // only run conversion when there is a mobiledoc string

View File

@ -20,7 +20,7 @@ const GA_FEATURES = [
'outboundLinkTagging',
'announcementBar',
'signupForm',
'lexicalEditor'
'recommendations'
];
// NOTE: this allowlist is meant to be used to filter out any unexpected
@ -41,8 +41,9 @@ const ALPHA_FEATURES = [
'collectionsCard',
'tipsAndDonations',
'importMemberTier',
'recommendations',
'lexicalIndicators'
'lexicalIndicators',
'listUnsubscribeHeader',
'editorEmojiPicker'
];
module.exports.GA_KEYS = [...GA_FEATURES];

View File

@ -1,6 +1,6 @@
{
"name": "ghost",
"version": "5.70.1",
"version": "5.70.2",
"description": "The professional publishing platform",
"author": "Ghost Foundation",
"homepage": "https://ghost.org",
@ -53,7 +53,7 @@
"prepack": "monobundle"
},
"engines": {
"node": "^16.14.0 || ^18.12.1",
"node": "^18.12.1",
"cli": "^1.25.0"
},
"dependencies": {
@ -188,7 +188,7 @@
"glob": "8.1.0",
"got": "11.8.6",
"gscan": "4.39.4",
"human-number": "2.0.3",
"human-number": "2.0.4",
"image-size": "1.0.2",
"intl": "1.2.5",
"intl-messageformat": "5.4.3",
@ -209,7 +209,7 @@
"multer": "1.4.4",
"mysql2": "3.6.2",
"nconf": "0.12.1",
"newrelic": "11.2.1",
"newrelic": "11.4.0",
"node-jose": "2.2.0",
"path-match": "1.2.4",
"probe-image-size": "7.2.3",

File diff suppressed because one or more lines are too long

View File

@ -788,10 +788,10 @@ Object {
"frontmatter": null,
"html": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"ghostVersion\\":\\"4.0\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"\\"]]]]}",
"mobiledoc": null,
"og_description": null,
"og_image": null,
"og_title": null,
@ -878,10 +878,10 @@ Object {
"featured": false,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"ghostVersion\\":\\"4.0\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"\\"]]]]}",
"mobiledoc": null,
"og_description": null,
"og_image": null,
"og_title": null,
@ -947,113 +947,7 @@ exports[`Pages API Copy Can copy a page 3: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "3779",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/pages\\\\/\\[a-f0-9\\]\\{24\\}\\\\//,
"vary": "Accept-Version, Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Pages API Create Can create a page with html (labs.lexicalEditor) 1: [body] 1`] = `
Object {
"pages": Array [
Object {
"authors": Any<Array>,
"canonical_url": null,
"codeinjection_foot": null,
"codeinjection_head": null,
"comment_id": Any<String>,
"count": Object {
"negative_feedback": 0,
"paid_conversions": 0,
"positive_feedback": 0,
"signups": 0,
},
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"custom_excerpt": null,
"custom_template": null,
"excerpt": "Testing page creation with html",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
"featured": false,
"frontmatter": null,
"html": "<p>Testing page creation with html</p>",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[{\\"detail\\":0,\\"format\\":0,\\"mode\\":\\"normal\\",\\"style\\":\\"\\",\\"text\\":\\"Testing page creation with html\\",\\"type\\":\\"extended-text\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": null,
"og_description": null,
"og_image": null,
"og_title": null,
"primary_author": Any<Object>,
"primary_tag": Any<Object>,
"published_at": null,
"reading_time": 0,
"show_title_and_feature_image": Any<Boolean>,
"slug": "html-test-2",
"status": "draft",
"tags": Any<Array>,
"tiers": Array [
Object {
"active": true,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"currency": null,
"description": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"monthly_price": null,
"monthly_price_id": null,
"name": "Free",
"slug": "free",
"trial_days": 0,
"type": "free",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"visibility": "public",
"welcome_page_url": null,
"yearly_price": null,
"yearly_price_id": null,
},
Object {
"active": true,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"currency": "usd",
"description": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"monthly_price": 500,
"monthly_price_id": null,
"name": "Default Product",
"slug": "default-product",
"trial_days": 0,
"type": "paid",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"visibility": "public",
"welcome_page_url": null,
"yearly_price": 5000,
"yearly_price_id": null,
},
],
"title": "HTML test",
"twitter_description": null,
"twitter_image": null,
"twitter_title": null,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"url": Any<String>,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
"visibility": "public",
},
],
}
`;
exports[`Pages API Create Can create a page with html (labs.lexicalEditor) 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "4091",
"content-length": "3859",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -1089,10 +983,10 @@ Object {
"frontmatter": null,
"html": "<p>Testing page creation with html</p>",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[{\\"detail\\":0,\\"format\\":0,\\"mode\\":\\"normal\\",\\"style\\":\\"\\",\\"text\\":\\"Testing page creation with html\\",\\"type\\":\\"extended-text\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"atoms\\":[],\\"cards\\":[],\\"markups\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"Testing page creation with html\\"]]]]}",
"mobiledoc": null,
"og_description": null,
"og_image": null,
"og_title": null,
@ -1159,7 +1053,7 @@ exports[`Pages API Create Can create a page with html 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "3867",
"content-length": "4089",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -1289,10 +1183,10 @@ Object {
"frontmatter": null,
"html": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"ghostVersion\\":\\"4.0\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"\\"]]]]}",
"mobiledoc": null,
"og_description": null,
"og_image": null,
"og_title": null,
@ -1358,7 +1252,7 @@ exports[`Pages API Update Can modify show_title_and_feature_image property 2: [h
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "3780",
"content-length": "3860",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,

View File

@ -38,6 +38,7 @@ Object {
"featured": false,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"<h1>Welcome to my invisible post!</h1>\\"}]],\\"sections\\":[[10,0]]}",
@ -126,6 +127,7 @@ Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac tu
"featured": false,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"meta_description": "meta description for draft post",
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"<h1>HTML Ipsum Presents</h1><p><strong>Pellentesque habitant morbi tristique</strong> senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. <em>Aenean ultricies mi vitae est.</em> Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, <code>commodo vitae</code>, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui. <a href=\\\\\\"#\\\\\\">Donec non enim</a> in turpis pulvinar facilisis. Ut felis.</p><h2>Header Level 2</h2><ol><li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li><li>Aliquam tincidunt mauris eu risus.</li></ol><blockquote><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus pretium ornare est.</p></blockquote><h3>Header Level 3</h3><ul><li>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</li><li>Aliquam tincidunt mauris eu risus.</li></ul><pre><code>#header h1 a{display: block;width: 300px;height: 80px;}</code></pre>\\"}]],\\"sections\\":[[10,0]]}",
@ -195,7 +197,7 @@ exports[`Posts API Can browse 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "10706",
"content-length": "10736",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -253,6 +255,7 @@ Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut
"featured": true,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"<p><nav><ul><li><a href=\\\\\\"#nowhere\\\\\\" title=\\\\\\"Anchor URL\\\\\\">Lorem</a></li><li><a href=\\\\\\"http://127.0.0.1:2369/about#nowhere\\\\\\" title=\\\\\\"Relative URL\\\\\\">Aliquam</a></li><li><a href=\\\\\\"//somewhere.com/link#nowhere\\\\\\" title=\\\\\\"Protocol Relative URL\\\\\\">Tortor</a></li><li><a href=\\\\\\"http://somewhere.com/link#nowhere\\\\\\" title=\\\\\\"Absolute URL\\\\\\">Morbi</a></li><li><a href=\\\\\\"#nowhere\\\\\\" title=\\\\\\"Praesent dapibus, neque id cursus faucibus\\\\\\">Praesent</a></li><li><a href=\\\\\\"#nowhere\\\\\\" title=\\\\\\"Pellentesque fermentum dolor\\\\\\">Pellentesque</a></li></ul></nav><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p><table><thead><tr><th>1</th><th>2</th><th>3</th><th>4</th></tr></thead><tbody><tr><td>a</td><td>b</td><td>c</td><td>d</td></tr><tr><td>e</td><td>f</td><td>g</td><td>h</td></tr><tr><td>i</td><td>j</td><td>k</td><td>l</td></tr></tbody></table><dl><dt>Definition list</dt><dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd><dt>Lorem ipsum dolor sit amet</dt><dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd></dl><ul><li>Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in, pharetra a, ultricies in, diam. Sed arcu. Cras consequat.</li><li>Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus.</li><li>Phasellus ultrices nulla quis nibh. Quisque a lectus. Donec consectetuer ligula vulputate sem tristique cursus. Nam nulla quam, gravida non, commodo a, sodales sit amet, nisi.</li><li>Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices ut, elementum vulputate, nunc.</li></ul></p>\\"}]],\\"sections\\":[[10,0]]}",
@ -348,6 +351,7 @@ mctesters
"featured": true,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"meta_description": "meta description for short and sweet",
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"## testing\\\\n\\\\nmctesters\\\\n\\\\n- test\\\\n- line\\\\n- items\\"}]],\\"sections\\":[[10,0]]}",
@ -417,7 +421,7 @@ exports[`Posts API Can browse filtering by a collection 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "11701",
"content-length": "11731",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -464,6 +468,7 @@ Object {
"featured": false,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"atoms\\":[],\\"cards\\":[[\\"image\\",{\\"src\\":\\"https://static.ghost.org/v4.0.0/images/portalsettings.png\\",\\"width\\":2924,\\"height\\":1810,\\"cardWidth\\":\\"wide\\"}],[\\"hr\\",{}]],\\"markups\\":[[\\"em\\"],[\\"a\\",[\\"href\\",\\"#/portal\\"]],[\\"a\\",[\\"href\\",\\"http://127.0.0.1:2369/sell/\\"]]],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"What sets Ghost apart from other products is that you can publish content and grow your audience using the same platform. Rather than just endlessly posting and hoping someone is listening, you can track real signups against your work and have them subscribe to be notified of future posts. The feature that makes all this possible is called \\"],[0,[0],1,\\"Portal\\"],[0,[],0,\\".\\"]]],[1,\\"p\\",[[0,[],0,\\"Portal is an embedded interface for your audience to sign up to your site. It works on every Ghost site, with every theme, and for any type of publisher. \\"]]],[1,\\"p\\",[[0,[],0,\\"You can customize the design, content and settings of Portal to suit your site, whether you just want people to sign up to your newsletter — or you're running a full premium publication with user sign-ins and private content.\\"]]],[10,0],[1,\\"p\\",[[0,[],0,\\"Once people sign up to your site, they'll receive an email confirmation with a link to click. The link acts as an automatic sign-in, so subscribers will be automatically signed-in to your site when they click on it. There are a couple of interesting angles to this:\\"]]],[1,\\"p\\",[[0,[],0,\\"Because subscribers are automatically able to sign in and out of your site as registered members: You can (optionally) restrict access to posts and pages depending on whether people are signed-in or not. So if you want to publish some posts for free, but keep some really great stuff for members-only, this can be a great draw to encourage people to sign up!\\"]]],[1,\\"p\\",[[0,[],0,\\"Ghost members sign in using email authentication links, so there are no passwords for people to set or forget. You can turn any list of email subscribers into a database of registered members who can sign in to your site. Like magic.\\"]]],[1,\\"p\\",[[0,[],0,\\"Portal makes all of this possible, and it appears by default as a floating button in the bottom-right corner of your site. When people are logged out, clicking it will open a sign-up/sign-in window. When members are logged in, clicking the Portal button will open the account menu where they can edit their name, email, and subscription settings.\\"]]],[1,\\"p\\",[[0,[],0,\\"The floating Portal button is completely optional. If you prefer, you can add manual links to your content, navigation, or theme to trigger it instead.\\"]]],[1,\\"p\\",[[0,[],0,\\"Like this! \\"],[0,[1],1,\\"Sign up here\\"]]],[10,1],[1,\\"p\\",[[0,[],0,\\"As you start to grow your registered audience, you'll be able to get a sense of who you're publishing \\"],[0,[0],1,\\"for\\"],[0,[],0,\\" and where those people are coming \\"],[0,[0],1,\\"from\\"],[0,[],0,\\". Best of all: You'll have a straightforward, reliable way to connect with people who enjoy your work.\\"]]],[1,\\"p\\",[[0,[],0,\\"Social networks go in and out of fashion all the time. Email addresses are timeless.\\"]]],[1,\\"p\\",[[0,[],0,\\"Growing your audience is valuable no matter what type of site you run, but if your content \\"],[0,[0],1,\\"is\\"],[0,[],0,\\" your business, then you might also be interested in \\"],[0,[2],1,\\"setting up premium subscriptions\\"],[0,[],0,\\".\\"]]]],\\"ghostVersion\\":\\"4.0\\"}",
@ -533,7 +538,7 @@ exports[`Posts API Can browse filtering by collection using paging parameters 2:
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "8802",
"content-length": "8817",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -1015,10 +1020,10 @@ Object {
"featured": false,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"ghostVersion\\":\\"4.0\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"\\"]]]]}",
"mobiledoc": null,
"newsletter": null,
"og_description": null,
"og_image": null,
@ -1084,116 +1089,7 @@ exports[`Posts API Copy Can copy a post 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "3814",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
"location": StringMatching /https\\?:\\\\/\\\\/\\.\\*\\?\\\\/posts\\\\/\\[a-f0-9\\]\\{24\\}\\\\//,
"vary": "Accept-Version, Origin, Accept-Encoding",
"x-powered-by": "Express",
}
`;
exports[`Posts API Create Can create a post with html (labs.lexicalEditor) 1: [body] 1`] = `
Object {
"posts": Array [
Object {
"authors": Any<Array>,
"canonical_url": null,
"codeinjection_foot": null,
"codeinjection_head": null,
"comment_id": Any<String>,
"count": Object {
"clicks": 0,
"negative_feedback": 0,
"positive_feedback": 0,
},
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"custom_excerpt": null,
"custom_template": null,
"email": null,
"email_only": false,
"email_segment": "all",
"email_subject": null,
"excerpt": "Testing post creation with html",
"feature_image": null,
"feature_image_alt": null,
"feature_image_caption": null,
"featured": false,
"frontmatter": null,
"html": "<p>Testing post creation with html</p>",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[{\\"detail\\":0,\\"format\\":0,\\"mode\\":\\"normal\\",\\"style\\":\\"\\",\\"text\\":\\"Testing post creation with html\\",\\"type\\":\\"extended-text\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": null,
"newsletter": null,
"og_description": null,
"og_image": null,
"og_title": null,
"primary_author": Any<Object>,
"primary_tag": Any<Object>,
"published_at": null,
"reading_time": 0,
"slug": "html-test-2",
"status": "draft",
"tags": Any<Array>,
"tiers": Array [
Object {
"active": true,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"currency": null,
"description": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"monthly_price": null,
"monthly_price_id": null,
"name": "Free",
"slug": "free",
"trial_days": 0,
"type": "free",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"visibility": "public",
"welcome_page_url": null,
"yearly_price": null,
"yearly_price_id": null,
},
Object {
"active": true,
"created_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"currency": "usd",
"description": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"monthly_price": 500,
"monthly_price_id": null,
"name": "Default Product",
"slug": "default-product",
"trial_days": 0,
"type": "paid",
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"visibility": "public",
"welcome_page_url": null,
"yearly_price": 5000,
"yearly_price_id": null,
},
],
"title": "HTML test",
"twitter_description": null,
"twitter_image": null,
"twitter_title": null,
"updated_at": StringMatching /\\\\d\\{4\\}-\\\\d\\{2\\}-\\\\d\\{2\\}T\\\\d\\{2\\}:\\\\d\\{2\\}:\\\\d\\{2\\}\\\\\\.000Z/,
"url": Any<String>,
"uuid": StringMatching /\\[a-f0-9\\]\\{8\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{4\\}-\\[a-f0-9\\]\\{12\\}/,
"visibility": "public",
},
],
}
`;
exports[`Posts API Create Can create a post with html (labs.lexicalEditor) 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "4126",
"content-length": "3894",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -1232,10 +1128,10 @@ Object {
"frontmatter": null,
"html": "<p>Testing post creation with html</p>",
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[{\\"detail\\":0,\\"format\\":0,\\"mode\\":\\"normal\\",\\"style\\":\\"\\",\\"text\\":\\"Testing post creation with html\\",\\"type\\":\\"extended-text\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"atoms\\":[],\\"cards\\":[],\\"markups\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"Testing post creation with html\\"]]]]}",
"mobiledoc": null,
"newsletter": null,
"og_description": null,
"og_image": null,
@ -1302,7 +1198,7 @@ exports[`Posts API Create Can create a post with html 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "3902",
"content-length": "4124",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -1641,6 +1537,7 @@ Definition listConsectetur adipisicing elit, sed do eiusmod tempor incididunt ut
"featured": true,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"<p><nav><ul><li><a href=\\\\\\"#nowhere\\\\\\" title=\\\\\\"Anchor URL\\\\\\">Lorem</a></li><li><a href=\\\\\\"http://127.0.0.1:2369/about#nowhere\\\\\\" title=\\\\\\"Relative URL\\\\\\">Aliquam</a></li><li><a href=\\\\\\"//somewhere.com/link#nowhere\\\\\\" title=\\\\\\"Protocol Relative URL\\\\\\">Tortor</a></li><li><a href=\\\\\\"http://somewhere.com/link#nowhere\\\\\\" title=\\\\\\"Absolute URL\\\\\\">Morbi</a></li><li><a href=\\\\\\"#nowhere\\\\\\" title=\\\\\\"Praesent dapibus, neque id cursus faucibus\\\\\\">Praesent</a></li><li><a href=\\\\\\"#nowhere\\\\\\" title=\\\\\\"Pellentesque fermentum dolor\\\\\\">Pellentesque</a></li></ul></nav><p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p><table><thead><tr><th>1</th><th>2</th><th>3</th><th>4</th></tr></thead><tbody><tr><td>a</td><td>b</td><td>c</td><td>d</td></tr><tr><td>e</td><td>f</td><td>g</td><td>h</td></tr><tr><td>i</td><td>j</td><td>k</td><td>l</td></tr></tbody></table><dl><dt>Definition list</dt><dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd><dt>Lorem ipsum dolor sit amet</dt><dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd></dl><ul><li>Morbi in sem quis dui placerat ornare. Pellentesque odio nisi, euismod in, pharetra a, ultricies in, diam. Sed arcu. Cras consequat.</li><li>Praesent dapibus, neque id cursus faucibus, tortor neque egestas augue, eu vulputate magna eros eu erat. Aliquam erat volutpat. Nam dui mi, tincidunt quis, accumsan porttitor, facilisis luctus, metus.</li><li>Phasellus ultrices nulla quis nibh. Quisque a lectus. Donec consectetuer ligula vulputate sem tristique cursus. Nam nulla quam, gravida non, commodo a, sodales sit amet, nisi.</li><li>Pellentesque fermentum dolor. Aliquam quam lectus, facilisis auctor, ultrices ut, elementum vulputate, nunc.</li></ul></p>\\"}]],\\"sections\\":[[10,0]]}",
@ -1736,6 +1633,7 @@ mctesters
"featured": true,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": null,
"meta_description": "meta description for short and sweet",
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[[\\"markdown\\",{\\"markdown\\":\\"## testing\\\\n\\\\nmctesters\\\\n\\\\n- test\\\\n- line\\\\n- items\\"}]],\\"sections\\":[[10,0]]}",
@ -1805,7 +1703,7 @@ exports[`Posts API Delete Can delete posts belonging to a collection and returns
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "11701",
"content-length": "11731",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -2019,9 +1917,10 @@ Object {
"featured": false,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"ghostVersion\\":\\"4.0\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"\\"]]]]}",
"mobiledoc": null,
"newsletter": null,
"og_description": null,
"og_image": null,
@ -2087,7 +1986,7 @@ exports[`Posts API Update Can add and remove collections 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "3813",
"content-length": "3908",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -2214,10 +2113,6 @@ Object {
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"sort_order": 20,
},
Object {
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"sort_order": 21,
},
],
"slug": "latest",
"title": "Latest",
@ -2245,9 +2140,10 @@ Object {
"featured": false,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"ghostVersion\\":\\"4.0\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"\\"]]]]}",
"mobiledoc": null,
"newsletter": null,
"og_description": null,
"og_image": null,
@ -2313,7 +2209,7 @@ exports[`Posts API Update Can add and remove collections 4: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "5459",
"content-length": "5504",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,
@ -2440,10 +2336,6 @@ Object {
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"sort_order": 20,
},
Object {
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"sort_order": 21,
},
],
"slug": "latest",
"title": "Latest",
@ -2471,9 +2363,10 @@ Object {
"featured": false,
"frontmatter": null,
"id": StringMatching /\\[a-f0-9\\]\\{24\\}/,
"lexical": "{\\"root\\":{\\"children\\":[{\\"children\\":[],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"paragraph\\",\\"version\\":1}],\\"direction\\":null,\\"format\\":\\"\\",\\"indent\\":0,\\"type\\":\\"root\\",\\"version\\":1}}",
"meta_description": null,
"meta_title": null,
"mobiledoc": "{\\"version\\":\\"0.3.1\\",\\"ghostVersion\\":\\"4.0\\",\\"markups\\":[],\\"atoms\\":[],\\"cards\\":[],\\"sections\\":[[1,\\"p\\",[[0,[],0,\\"\\"]]]]}",
"mobiledoc": null,
"newsletter": null,
"og_description": null,
"og_image": null,
@ -2539,7 +2432,7 @@ exports[`Posts API Update Can add and remove collections 6: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "5453",
"content-length": "5498",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,

View File

@ -758,7 +758,7 @@ exports[`Settings API Edit Can edit a setting 2: [headers] 1`] = `
Object {
"access-control-allow-origin": "http://127.0.0.1:2369",
"cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0",
"content-length": "4278",
"content-length": "4280",
"content-type": "application/json; charset=utf-8",
"content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/,
"etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/,

View File

@ -38,7 +38,7 @@ describe('Pages API', function () {
jsonResponse.pages[1].url.should.eql(`${config.get('url')}/contribute/`);
});
it('Can retrieve pages with lexical format', async function () {
it('Can retrieve pages with just lexical format', async function () {
const res = await request.get(localUtils.API.getApiQuery('pages/?formats=lexical'))
.set('Origin', config.get('url'))
.expect('Content-Type', /json/)
@ -121,7 +121,7 @@ describe('Pages API', function () {
res.body.pages.length.should.eql(1);
const [returnedPage] = res.body.pages;
const additionalProperties = ['lexical', 'reading_time'];
const additionalProperties = ['reading_time'];
localUtils.API.checkResponse(returnedPage, 'page', additionalProperties);
should.equal(returnedPage.mobiledoc, page.mobiledoc);
@ -172,7 +172,7 @@ describe('Pages API', function () {
res.body.pages.length.should.eql(1);
const [returnedPage] = res.body.pages;
const additionalProperties = ['lexical', 'html', 'reading_time'];
const additionalProperties = ['html', 'reading_time'];
localUtils.API.checkResponse(returnedPage, 'page', additionalProperties);
should.equal(returnedPage.mobiledoc, null);

Some files were not shown because too many files have changed in this diff Show More