mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-22 19:32:54 +03:00
Temporarily fixed eslint rules being disabled in AdminX (#17565)
no issue
This commit is contained in:
parent
2b9e322b3e
commit
8000df963b
@ -17,6 +17,89 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
// ----------------------
|
||||
// Rules COPIED from base config, remove these when the config is fixed
|
||||
|
||||
// Style Rules
|
||||
// Require 4 spaces
|
||||
indent: ['error', 4],
|
||||
// Require single quotes for strings & properties (allows template literals)
|
||||
quotes: ['error', 'single', {allowTemplateLiterals: true}],
|
||||
'quote-props': ['error', 'as-needed'],
|
||||
// Require semi colons, always at the end of a line
|
||||
semi: ['error', 'always'],
|
||||
'semi-style': ['error', 'last'],
|
||||
// Don't allow dangling commas
|
||||
'comma-dangle': ['error', 'never'],
|
||||
// Always require curly braces, and position them at the end and beginning of lines
|
||||
curly: 'error',
|
||||
'brace-style': ['error', '1tbs'],
|
||||
// Don't allow padding inside of blocks
|
||||
'padded-blocks': ['error', 'never'],
|
||||
// Require objects to be consistently formatted with newlines
|
||||
'object-curly-newline': ['error', {consistent: true}],
|
||||
// Don't allow more than 1 consecutive empty line or an empty 1st line
|
||||
'no-multiple-empty-lines': ['error', {max: 1, maxBOF: 0}],
|
||||
// Variables must be camelcase, but properties are not checked
|
||||
camelcase: ['error', {properties: 'never'}],
|
||||
// Allow newlines before dots, not after e.g. .then goes on a new line
|
||||
'dot-location': ['error', 'property'],
|
||||
// Prefer dot notation over array notation
|
||||
'dot-notation': ['error'],
|
||||
|
||||
// Spacing rules
|
||||
// Don't allow multiple spaces anywhere
|
||||
'no-multi-spaces': 'error',
|
||||
// Anonymous functions have a sape, named functions never do
|
||||
'space-before-function-paren': ['error', {anonymous: 'always', named: 'never'}],
|
||||
// Don't put spaces inside of objects or arrays
|
||||
'object-curly-spacing': ['error', 'never'],
|
||||
'array-bracket-spacing': ['error', 'never'],
|
||||
// Allow a max of one space between colons and values
|
||||
'key-spacing': ['error', {mode: 'strict'}],
|
||||
// Require spaces before and after keywords like if, else, try, catch etc
|
||||
'keyword-spacing': 'error',
|
||||
// No spaces around semis
|
||||
'semi-spacing': 'error',
|
||||
// 1 space around arrows
|
||||
'arrow-spacing': 'error',
|
||||
// Don't allow spaces inside parenthesis
|
||||
'space-in-parens': ['error', 'never'],
|
||||
// Require single spaces either side of operators
|
||||
'space-unary-ops': 'error',
|
||||
'space-infix-ops': 'error',
|
||||
|
||||
// Best practice rules
|
||||
// Require === / !==
|
||||
eqeqeq: ['error', 'always'],
|
||||
// Don't allow ++ and --
|
||||
'no-plusplus': ['error', {allowForLoopAfterthoughts: true}],
|
||||
// Don't allow eval
|
||||
'no-eval': 'error',
|
||||
// Throw errors for unnecessary usage of .call or .apply
|
||||
'no-useless-call': 'error',
|
||||
// Don't allow console.* calls
|
||||
'no-console': 'error',
|
||||
// Prevent [variable shadowing](https://en.wikipedia.org/wiki/Variable_shadowing)
|
||||
'no-shadow': ['error'],
|
||||
|
||||
// Return rules
|
||||
// Prevent missing return statements in array functions like map & reduce
|
||||
'array-callback-return': 'error',
|
||||
'no-constructor-return': 'error',
|
||||
'no-promise-executor-return': 'error',
|
||||
|
||||
// Arrow function styles
|
||||
// Do not enforce single lines when using arrow functions.
|
||||
// https://eslint.org/docs/rules/arrow-body-style
|
||||
'arrow-body-style': 'off',
|
||||
'arrow-parens': ['error', 'as-needed', {requireForBlockBody: true}],
|
||||
'implicit-arrow-linebreak': 'error',
|
||||
'no-confusing-arrow': 'error',
|
||||
|
||||
// ----------------------
|
||||
// Rules NOT COPIED from base config, keep these
|
||||
|
||||
// sort multiple import lines into alphabetical groups
|
||||
'ghost/sort-imports-es6-autofix/sort-imports-es6': ['error', {
|
||||
memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple']
|
||||
|
@ -1,12 +1,12 @@
|
||||
import ChangeThemeModal from "../settings/site/ThemeModal";
|
||||
import DesignModal from "../settings/site/DesignModal";
|
||||
import InviteUserModal from "../settings/general/InviteUserModal";
|
||||
import NavigationModal from "../settings/site/NavigationModal";
|
||||
import NiceModal from "@ebay/nice-modal-react";
|
||||
import PortalModal from "../settings/membership/portal/PortalModal";
|
||||
import React, { createContext, useCallback, useEffect, useState } from "react";
|
||||
import StripeConnectModal from "../settings/membership/stripe/StripeConnectModal";
|
||||
import TierDetailModal from "../settings/membership/tiers/TierDetailModal";
|
||||
import ChangeThemeModal from '../settings/site/ThemeModal';
|
||||
import DesignModal from '../settings/site/DesignModal';
|
||||
import InviteUserModal from '../settings/general/InviteUserModal';
|
||||
import NavigationModal from '../settings/site/NavigationModal';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import PortalModal from '../settings/membership/portal/PortalModal';
|
||||
import React, {createContext, useCallback, useEffect, useState} from 'react';
|
||||
import StripeConnectModal from '../settings/membership/stripe/StripeConnectModal';
|
||||
import TierDetailModal from '../settings/membership/tiers/TierDetailModal';
|
||||
|
||||
type RoutingContextProps = {
|
||||
route: string;
|
||||
@ -17,11 +17,11 @@ type RoutingContextProps = {
|
||||
};
|
||||
|
||||
export const RouteContext = createContext<RoutingContextProps>({
|
||||
route: "",
|
||||
scrolledRoute: "",
|
||||
route: '',
|
||||
scrolledRoute: '',
|
||||
yScroll: 0,
|
||||
updateRoute: () => {},
|
||||
updateScrolled: () => {},
|
||||
updateScrolled: () => {}
|
||||
});
|
||||
|
||||
function getHashPath(urlPath: string | undefined) {
|
||||
@ -41,7 +41,7 @@ function getHashPath(urlPath: string | undefined) {
|
||||
const scrollToSectionGroup = (pathName: string) => {
|
||||
const element = document.getElementById(pathName);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth" });
|
||||
element.scrollIntoView({behavior: 'smooth'});
|
||||
}
|
||||
};
|
||||
|
||||
@ -56,19 +56,19 @@ const handleNavigation = (scroll: boolean = true) => {
|
||||
const pathName = getHashPath(hash);
|
||||
|
||||
if (pathName) {
|
||||
if (pathName === "design/edit/themes") {
|
||||
if (pathName === 'design/edit/themes') {
|
||||
NiceModal.show(ChangeThemeModal);
|
||||
} else if (pathName === "design/edit") {
|
||||
} else if (pathName === 'design/edit') {
|
||||
NiceModal.show(DesignModal);
|
||||
} else if (pathName === "navigation/edit") {
|
||||
} else if (pathName === 'navigation/edit') {
|
||||
NiceModal.show(NavigationModal);
|
||||
} else if (pathName === "users/invite") {
|
||||
} else if (pathName === 'users/invite') {
|
||||
NiceModal.show(InviteUserModal);
|
||||
} else if (pathName === "portal/edit") {
|
||||
} else if (pathName === 'portal/edit') {
|
||||
NiceModal.show(PortalModal);
|
||||
} else if (pathName === "tiers/add") {
|
||||
} else if (pathName === 'tiers/add') {
|
||||
NiceModal.show(TierDetailModal);
|
||||
} else if (pathName === "stripe-connect") {
|
||||
} else if (pathName === 'stripe-connect') {
|
||||
NiceModal.show(StripeConnectModal);
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ const handleNavigation = (scroll: boolean = true) => {
|
||||
|
||||
return pathName;
|
||||
}
|
||||
return "";
|
||||
return '';
|
||||
};
|
||||
|
||||
type RouteProviderProps = {
|
||||
@ -86,9 +86,9 @@ type RouteProviderProps = {
|
||||
};
|
||||
|
||||
const RoutingProvider: React.FC<RouteProviderProps> = ({children}) => {
|
||||
const [route, setRoute] = useState<string>("");
|
||||
const [route, setRoute] = useState<string>('');
|
||||
const [yScroll, setYScroll] = useState(0);
|
||||
const [scrolledRoute, setScrolledRoute] = useState<string>("");
|
||||
const [scrolledRoute, setScrolledRoute] = useState<string>('');
|
||||
|
||||
const updateRoute = useCallback(
|
||||
(newPath: string) => {
|
||||
@ -116,21 +116,21 @@ const RoutingProvider: React.FC<RouteProviderProps> = ({ children }) => {
|
||||
};
|
||||
|
||||
const handleScroll = () => {
|
||||
const element = document.getElementById("admin-x-root");
|
||||
const element = document.getElementById('admin-x-root');
|
||||
const scrollPosition = element!.scrollTop;
|
||||
setYScroll(scrollPosition);
|
||||
};
|
||||
|
||||
const element = document.getElementById("admin-x-root");
|
||||
const element = document.getElementById('admin-x-root');
|
||||
const matchedRoute = handleNavigation();
|
||||
setRoute(matchedRoute);
|
||||
element!.addEventListener("scroll", handleScroll);
|
||||
element!.addEventListener('scroll', handleScroll);
|
||||
|
||||
window.addEventListener("hashchange", handleHashChange);
|
||||
window.addEventListener('hashchange', handleHashChange);
|
||||
|
||||
return () => {
|
||||
element!.removeEventListener("scroll", handleScroll);
|
||||
window.removeEventListener("hashchange", handleHashChange);
|
||||
element!.removeEventListener('scroll', handleScroll);
|
||||
window.removeEventListener('hashchange', handleHashChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@ -141,7 +141,7 @@ const RoutingProvider: React.FC<RouteProviderProps> = ({ children }) => {
|
||||
scrolledRoute,
|
||||
yScroll,
|
||||
updateRoute,
|
||||
updateScrolled,
|
||||
updateScrolled
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -248,7 +248,7 @@ const NewsletterDetailModal: React.FC<NewsletterDetailModalProps> = ({newsletter
|
||||
await editNewsletter(formState);
|
||||
modal.remove();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const updateNewsletter = (fields: Partial<Newsletter>) => {
|
||||
updateForm(state => ({...state, ...fields}));
|
||||
|
@ -35,7 +35,7 @@ const NewsletterPreview: React.FC<{newsletter: Newsletter}> = ({newsletter}) =>
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const showCommentCta = newsletter.show_comment_cta && commentsEnabled !== 'off';
|
||||
const showFeedback = newsletter.feedback_enabled && config.labs.audienceFeedback
|
||||
const showFeedback = newsletter.feedback_enabled && config.labs.audienceFeedback;
|
||||
|
||||
return (
|
||||
<div className="relative flex grow flex-col">
|
||||
|
@ -104,7 +104,7 @@ const BasicInputs: React.FC<UserDetailProps> = ({errors, validators, user, setUs
|
||||
<SettingGroupContent>
|
||||
<TextField
|
||||
error={!!errors?.name}
|
||||
hint={errors?.name || "Use real name so people can recognize you"}
|
||||
hint={errors?.name || 'Use real name so people can recognize you'}
|
||||
title="Full name"
|
||||
value={user.name}
|
||||
onBlur={(e) => {
|
||||
@ -633,7 +633,7 @@ const UserDetailModal:React.FC<UserDetailModalProps> = ({user}) => {
|
||||
if (error) {
|
||||
showToast({
|
||||
type: 'pageError',
|
||||
message: "Can't save user! One or more fields have errors, please doublecheck you filled all mandatory fields"
|
||||
message: 'Can\'t save user! One or more fields have errors, please doublecheck you filled all mandatory fields'
|
||||
});
|
||||
setSaveState('');
|
||||
return;
|
||||
|
@ -44,11 +44,11 @@ const Tiers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
}
|
||||
];
|
||||
|
||||
let content
|
||||
let content;
|
||||
if (checkStripeEnabled(settings, config)) {
|
||||
content = <TabView selectedTab={selectedTab} tabs={tabs} onTabChange={setSelectedTab} />
|
||||
content = <TabView selectedTab={selectedTab} tabs={tabs} onTabChange={setSelectedTab} />;
|
||||
} else {
|
||||
content = <TiersList tab='free-tier' tiers={activeTiers.filter(tier => tier.type === 'free')} />
|
||||
content = <TiersList tab='free-tier' tiers={activeTiers.filter(tier => tier.type === 'free')} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -64,7 +64,7 @@ const Connect: React.FC = () => {
|
||||
|
||||
const saveTier = async () => {
|
||||
const {data} = await fetchActiveTiers();
|
||||
const tier = data?.tiers[0]
|
||||
const tier = data?.tiers[0];
|
||||
|
||||
if (tier) {
|
||||
tier.monthly_price = 500;
|
||||
@ -75,7 +75,7 @@ const Connect: React.FC = () => {
|
||||
/** To allow Stripe config to be ready in backend, we poll the save tier request */
|
||||
while (pollTimeout < RETRY_PRODUCT_SAVE_MAX_POLL) {
|
||||
await new Promise((resolve) => {
|
||||
setTimeout(resolve, RETRY_PRODUCT_SAVE_POLL_LENGTH)
|
||||
setTimeout(resolve, RETRY_PRODUCT_SAVE_POLL_LENGTH);
|
||||
});
|
||||
|
||||
try {
|
||||
@ -118,7 +118,7 @@ const Connect: React.FC = () => {
|
||||
} else {
|
||||
setError('Please enter a secure key');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const {apiRoot} = getGhostPaths();
|
||||
const stripeConnectUrl = `${apiRoot}/members/stripe_connect?mode=${testMode ? 'test' : 'live'}`;
|
||||
@ -160,7 +160,7 @@ const Connected: React.FC<{onClose?: () => void}> = ({onClose}) => {
|
||||
|
||||
const openDisconnectStripeModal = async () => {
|
||||
const {data} = await fetchMembers();
|
||||
const hasActiveStripeSubscriptions = Boolean(data?.meta?.pagination.total)
|
||||
const hasActiveStripeSubscriptions = Boolean(data?.meta?.pagination.total);
|
||||
|
||||
// const hasActiveStripeSubscriptions = false; //...
|
||||
// this.ghostPaths.url.api('/members/') + '?filter=status:paid&limit=0';
|
||||
@ -233,20 +233,20 @@ const Direct: React.FC<{onClose: () => void}> = ({onClose}) => {
|
||||
showToast({
|
||||
type: 'pageError',
|
||||
message: 'Failed to save settings. Please check you copied both keys correctly.'
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Heading level={3}>Connect Stripe</Heading>
|
||||
<Form marginBottom={false} marginTop>
|
||||
<TextField title='Publishable key' value={publishableKey?.toString()} onChange={(e) => updateSetting('stripe_publishable_key', e.target.value)} />
|
||||
<TextField title='Secure key' value={secretKey?.toString()} onChange={(e) => updateSetting('stripe_secret_key', e.target.value)} />
|
||||
<TextField title='Publishable key' value={publishableKey?.toString()} onChange={e => updateSetting('stripe_publishable_key', e.target.value)} />
|
||||
<TextField title='Secure key' value={secretKey?.toString()} onChange={e => updateSetting('stripe_secret_key', e.target.value)} />
|
||||
<Button className='mt-5' color='green' disabled={saveState === 'saving'} label='Save Stripe settings' onClick={onSubmit} />
|
||||
</Form>
|
||||
</div>
|
||||
@ -256,7 +256,7 @@ const Direct: React.FC<{onClose: () => void}> = ({onClose}) => {
|
||||
const StripeConnectModal: React.FC = () => {
|
||||
const {config} = useGlobalData();
|
||||
const {settings} = useSettings();
|
||||
const stripeConnectAccountId = getSettingValue(settings, 'stripe_connect_account_id')
|
||||
const stripeConnectAccountId = getSettingValue(settings, 'stripe_connect_account_id');
|
||||
const {updateRoute} = useRouting();
|
||||
const [step, setStep] = useState<'start' | 'connect'>('start');
|
||||
const mainModal = useModal();
|
||||
|
@ -123,7 +123,7 @@ const TierDetailModal: React.FC<TierDetailModalProps> = ({tier}) => {
|
||||
if (Object.values(validators).filter(validator => validator()).length) {
|
||||
showToast({
|
||||
type: 'pageError',
|
||||
message: "Can't save tier! One or more fields have errors, please doublecheck you filled all mandatory fields"
|
||||
message: 'Can\'t save tier! One or more fields have errors, please doublecheck you filled all mandatory fields'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ const ThemeSetting: React.FC<{
|
||||
const ThemeSettings: React.FC<{ settings: CustomThemeSetting[], updateSetting: (setting: CustomThemeSetting) => void }> = ({settings, updateSetting}) => {
|
||||
return (
|
||||
<SettingGroupContent className='mt-7'>
|
||||
{settings.map(setting => <ThemeSetting key={setting.key} setSetting={(value) => updateSetting({...setting, value} as CustomThemeSetting)} setting={setting} />)}
|
||||
{settings.map(setting => <ThemeSetting key={setting.key} setSetting={value => updateSetting({...setting, value} as CustomThemeSetting)} setting={setting} />)}
|
||||
</SettingGroupContent>
|
||||
);
|
||||
};
|
||||
|
@ -44,7 +44,7 @@ export const useBrowseUsers = createQuery<UsersResponseType>({
|
||||
export const useCurrentUser = createQuery<User>({
|
||||
dataType,
|
||||
path: '/users/me/',
|
||||
returnData: (originalData) => (originalData as UsersResponseType).users?.[0]
|
||||
returnData: originalData => (originalData as UsersResponseType).users?.[0]
|
||||
});
|
||||
|
||||
export const useEditUser = createMutation<UsersResponseType, User>({
|
||||
|
@ -68,7 +68,7 @@ export const useFetchApi = () => {
|
||||
|
||||
if (response.status > 299) {
|
||||
const data = response.headers.get('content-type')?.includes('application/json') ? await response.json() : undefined;
|
||||
throw new ApiError(response, data)
|
||||
throw new ApiError(response, data);
|
||||
} else if (response.status === 204) {
|
||||
return;
|
||||
} else {
|
||||
@ -139,11 +139,11 @@ const mutate = <ResponseData, Payload>({fetchApi, path, payload, searchParams, o
|
||||
const url = apiUrl(path, searchParams || defaultSearchParams);
|
||||
const generatedBody = payload && body?.(payload);
|
||||
|
||||
let requestBody: string | FormData | undefined = undefined
|
||||
let requestBody: string | FormData | undefined = undefined;
|
||||
if (generatedBody instanceof FormData) {
|
||||
requestBody = generatedBody
|
||||
requestBody = generatedBody;
|
||||
} else if (generatedBody) {
|
||||
requestBody = JSON.stringify(generatedBody)
|
||||
requestBody = JSON.stringify(generatedBody);
|
||||
}
|
||||
|
||||
return fetchApi(url, {
|
||||
|
@ -6,7 +6,7 @@ const settingsWithStripe = updatedSettingsResponse([
|
||||
{key: 'stripe_connect_secret_key', value: 'sk_test_123'},
|
||||
{key: 'stripe_connect_display_name', value: 'Dummy'},
|
||||
{key: 'stripe_connect_account_id', value: 'acct_123'}
|
||||
])
|
||||
]);
|
||||
|
||||
test.describe('Tier settings', async () => {
|
||||
test('Supports creating a new tier', async ({page}) => {
|
||||
@ -47,7 +47,7 @@ test.describe('Tier settings', async () => {
|
||||
visibility: 'public',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
|
||||
const lastApiRequests = await mockApi({page, responses: {
|
||||
tiers: {
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { ConfigResponseType } from '../../src/utils/api/config'
|
||||
import { CustomThemeSettingsResponseType } from '../../src/utils/api/customThemeSettings'
|
||||
import { ImagesResponseType } from '../../src/utils/api/images'
|
||||
import { InvitesResponseType } from '../../src/utils/api/invites'
|
||||
import { LabelsResponseType } from '../../src/utils/api/labels'
|
||||
import { OffersResponseType } from '../../src/utils/api/offers'
|
||||
import { Page, Request } from '@playwright/test'
|
||||
import { PostsResponseType } from '../../src/utils/api/posts'
|
||||
import { RolesResponseType } from '../../src/utils/api/roles'
|
||||
import { SettingsResponseType } from '../../src/utils/api/settings'
|
||||
import { SiteResponseType } from '../../src/utils/api/site'
|
||||
import { ThemesResponseType } from '../../src/utils/api/themes'
|
||||
import { TiersResponseType } from '../../src/utils/api/tiers'
|
||||
import { UsersResponseType } from '../../src/utils/api/users'
|
||||
import { readFileSync } from 'fs'
|
||||
import {ConfigResponseType} from '../../src/utils/api/config';
|
||||
import {CustomThemeSettingsResponseType} from '../../src/utils/api/customThemeSettings';
|
||||
import {ImagesResponseType} from '../../src/utils/api/images';
|
||||
import {InvitesResponseType} from '../../src/utils/api/invites';
|
||||
import {LabelsResponseType} from '../../src/utils/api/labels';
|
||||
import {OffersResponseType} from '../../src/utils/api/offers';
|
||||
import {Page, Request} from '@playwright/test';
|
||||
import {PostsResponseType} from '../../src/utils/api/posts';
|
||||
import {RolesResponseType} from '../../src/utils/api/roles';
|
||||
import {SettingsResponseType} from '../../src/utils/api/settings';
|
||||
import {SiteResponseType} from '../../src/utils/api/site';
|
||||
import {ThemesResponseType} from '../../src/utils/api/themes';
|
||||
import {TiersResponseType} from '../../src/utils/api/tiers';
|
||||
import {UsersResponseType} from '../../src/utils/api/users';
|
||||
import {readFileSync} from 'fs';
|
||||
|
||||
export const responseFixtures = {
|
||||
settings: JSON.parse(readFileSync(`${__dirname}/responses/settings.json`).toString()) as SettingsResponseType,
|
||||
|
Loading…
Reference in New Issue
Block a user