Merge branch 'main' into patch-1
49
.github/workflows/ci.yml
vendored
@ -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,
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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 />
|
||||
|
@ -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>
|
||||
|
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
11
apps/admin-x-settings/src/admin-x-ds/assets/icons/portal.svg
Normal 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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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',
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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]"
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -25,13 +25,6 @@ export const Default: Story = {
|
||||
}
|
||||
};
|
||||
|
||||
export const WithBackground: Story = {
|
||||
args: {
|
||||
options: options,
|
||||
clearBg: false
|
||||
}
|
||||
};
|
||||
|
||||
export const Black: Story = {
|
||||
args: {
|
||||
options: options,
|
||||
|
@ -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'
|
||||
);
|
||||
|
||||
|
@ -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',
|
||||
|
@ -46,6 +46,7 @@ export const WithTitle: Story = {
|
||||
|
||||
export const WithHint: Story = {
|
||||
args: {
|
||||
title: 'Description',
|
||||
placeholder: 'Enter description',
|
||||
hint: 'Here\'s some hint'
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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' />
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -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';
|
||||
}
|
||||
|
@ -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'>
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
}
|
||||
|
@ -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>
|
||||
}
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -63,6 +63,7 @@ const AddIntegrationModal: React.FC<RoutingModalProps> = () => {
|
||||
marginTop={false}
|
||||
>
|
||||
<TextField
|
||||
autoFocus={true}
|
||||
error={!!errors.name}
|
||||
hint={errors.name}
|
||||
placeholder='Custom integration'
|
||||
|
@ -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 = () => {
|
||||
|
@ -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')}
|
||||
/>
|
||||
|
@ -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}
|
||||
|
@ -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} />
|
||||
|
@ -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 you’d like them to be able to do.
|
||||
</p>
|
||||
<TextField
|
||||
clearBg={true}
|
||||
error={!!errors.email}
|
||||
hint={errors.email}
|
||||
inputRef={focusRef}
|
||||
|
@ -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>
|
||||
}
|
||||
]} />
|
||||
|
@ -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}
|
||||
|
@ -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'],
|
||||
|
@ -122,6 +122,7 @@ const Recommendations: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
|
||||
return (
|
||||
<SettingGroup
|
||||
beta={true}
|
||||
customButtons={buttons}
|
||||
description={groupDescription}
|
||||
keywords={keywords}
|
||||
|
@ -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 —</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}
|
||||
|
@ -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'
|
||||
|
@ -58,7 +58,7 @@ const NavigationModal = NiceModal.create(() => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className='mt-6'>
|
||||
<div className='mb-1 mt-6'>
|
||||
<TabView
|
||||
selectedTab={selectedTab}
|
||||
tabs={[
|
||||
|
@ -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}
|
||||
|
@ -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)}
|
||||
|
@ -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)}
|
||||
|
@ -126,6 +126,7 @@ const ThemePreview: React.FC<{
|
||||
fullWidth={false}
|
||||
options={variantOptions}
|
||||
selectedOption={selectedVariant}
|
||||
clearBg
|
||||
onSelect={(option) => {
|
||||
setSelectedVariant(option || undefined);
|
||||
}}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"
|
||||
>
|
||||
|
@ -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
|
||||
|
@ -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$/]
|
||||
|
@ -77,6 +77,7 @@ export default class FeatureService extends Service {
|
||||
@feature('tipsAndDonations') tipsAndDonations;
|
||||
@feature('recommendations') recommendations;
|
||||
@feature('lexicalIndicators') lexicalIndicators;
|
||||
@feature('editorEmojiPicker') editorEmojiPicker;
|
||||
|
||||
_user = null;
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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}}
|
||||
|
@ -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 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 '';
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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) {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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",
|
||||
|
@ -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 \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
|
@ -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 \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
|
@ -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 \\]\\|\\\\\\\\\\.\\)\\*"/,
|
||||
|
@ -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);
|
||||
|