mirror of
https://github.com/strapi/strapi.git
synced 2025-11-03 03:17:11 +00:00
Merge branch 'develop' into fix/relation-order-publish
This commit is contained in:
commit
bd3419429b
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 KiB |
@ -1,272 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Divider,
|
||||
Flex,
|
||||
FlexComponent,
|
||||
Popover,
|
||||
Typography,
|
||||
TypographyComponent,
|
||||
VisuallyHidden,
|
||||
} from '@strapi/design-system';
|
||||
import { Cross, Message, Play, Question, Book, PaperPlane } from '@strapi/icons';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
import onboardingPreview from '../assets/images/onboarding-preview.png';
|
||||
import { useAppInfo } from '../features/AppInfo';
|
||||
|
||||
const Onboarding = () => {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const { formatMessage } = useIntl();
|
||||
const communityEdition = useAppInfo('Onboarding', (state) => state.communityEdition);
|
||||
|
||||
const docLinks = [
|
||||
...DOCUMENTATION_LINKS,
|
||||
{
|
||||
label: { id: 'Settings.application.get-help', defaultMessage: 'Get help' },
|
||||
icon: Message,
|
||||
href: communityEdition
|
||||
? 'https://discord.strapi.io'
|
||||
: 'https://support.strapi.io/support/home',
|
||||
},
|
||||
];
|
||||
|
||||
const Icon = isOpen ? Cross : Question;
|
||||
|
||||
return (
|
||||
<Popover.Root onOpenChange={setIsOpen}>
|
||||
<Box position="fixed" bottom={2} right={2}>
|
||||
<Popover.Trigger>
|
||||
<HelperButton
|
||||
aria-label={formatMessage(
|
||||
isOpen
|
||||
? {
|
||||
id: 'app.components.Onboarding.help.button-close',
|
||||
defaultMessage: 'Close help menu',
|
||||
}
|
||||
: {
|
||||
id: 'app.components.Onboarding.help.button',
|
||||
defaultMessage: 'Open help menu',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<Icon fill="buttonNeutral0" />
|
||||
</HelperButton>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content align="end" side="top" sideOffset={12}>
|
||||
<Flex
|
||||
justifyContent="space-between"
|
||||
paddingBottom={5}
|
||||
paddingRight={6}
|
||||
paddingLeft={6}
|
||||
paddingTop={6}
|
||||
>
|
||||
<TypographyLineHeight fontWeight="bold">
|
||||
{formatMessage({
|
||||
id: 'app.components.Onboarding.title',
|
||||
defaultMessage: 'Get started videos',
|
||||
})}
|
||||
</TypographyLineHeight>
|
||||
<TextLink
|
||||
tag="a"
|
||||
href={WATCH_MORE.href}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
variant="pi"
|
||||
textColor="primary600"
|
||||
>
|
||||
{formatMessage(WATCH_MORE.label)}
|
||||
</TextLink>
|
||||
</Flex>
|
||||
<Divider />
|
||||
{VIDEO_LINKS.map(({ href, duration, label }, index) => (
|
||||
<VideoLinkWrapper
|
||||
tag="a"
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
key={href}
|
||||
hasRadius
|
||||
paddingTop={4}
|
||||
paddingBottom={4}
|
||||
paddingLeft={6}
|
||||
paddingRight={11}
|
||||
>
|
||||
<Box paddingRight={5}>
|
||||
<Number textColor="neutral200" variant="alpha">
|
||||
{index + 1}
|
||||
</Number>
|
||||
</Box>
|
||||
<Box position="relative">
|
||||
<Preview src={onboardingPreview} alt="" />
|
||||
<IconWrapper
|
||||
position="absolute"
|
||||
top="50%"
|
||||
left="50%"
|
||||
background="primary600"
|
||||
borderRadius="50%"
|
||||
justifyContent="center"
|
||||
width={6}
|
||||
height={6}
|
||||
>
|
||||
<Play fill="buttonNeutral0" width="12" height="12" />
|
||||
</IconWrapper>
|
||||
</Box>
|
||||
<Flex direction="column" alignItems="start" paddingLeft={4}>
|
||||
<Label fontWeight="bold">{formatMessage(label)}</Label>
|
||||
<VisuallyHidden>:</VisuallyHidden>
|
||||
<Typography textColor="neutral600" variant="pi">
|
||||
{duration}
|
||||
</Typography>
|
||||
</Flex>
|
||||
</VideoLinkWrapper>
|
||||
))}
|
||||
<Flex
|
||||
direction="column"
|
||||
alignItems="stretch"
|
||||
gap={2}
|
||||
paddingLeft={5}
|
||||
paddingTop={2}
|
||||
paddingBottom={5}
|
||||
>
|
||||
{docLinks.map(({ label, href, icon: Icon }) => (
|
||||
<Flex gap={3} key={href}>
|
||||
<Icon fill="primary600" />
|
||||
<TextLink
|
||||
tag="a"
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
variant="sigma"
|
||||
textColor="primary700"
|
||||
>
|
||||
{formatMessage(label)}
|
||||
</TextLink>
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
</Popover.Content>
|
||||
</Box>
|
||||
</Popover.Root>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: use new Button props derived from Box props with next DS release
|
||||
const HelperButton = styled(Button)`
|
||||
border-radius: 50%;
|
||||
padding: ${({ theme }) => theme.spaces[3]};
|
||||
/* Resetting 2rem height defined by Button component */
|
||||
height: unset;
|
||||
width: unset;
|
||||
|
||||
& > span {
|
||||
display: flex;
|
||||
|
||||
svg {
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const IconWrapper = styled<FlexComponent>(Flex)`
|
||||
transform: translate(-50%, -50%);
|
||||
`;
|
||||
|
||||
const Number = styled<TypographyComponent>(Typography)``;
|
||||
|
||||
const Label = styled<TypographyComponent>(Typography)``;
|
||||
|
||||
const VideoLinkWrapper = styled<FlexComponent<'a'>>(Flex)`
|
||||
text-decoration: none;
|
||||
|
||||
:focus-visible {
|
||||
outline-offset: ${({ theme }) => `-${theme.spaces[1]}`};
|
||||
}
|
||||
|
||||
:hover {
|
||||
background: ${({ theme }) => theme.colors.primary100};
|
||||
|
||||
/* Hover style for the number displayed */
|
||||
${Number} {
|
||||
color: ${({ theme }) => theme.colors.primary500};
|
||||
}
|
||||
|
||||
/* Hover style for the label */
|
||||
${Label} {
|
||||
color: ${({ theme }) => theme.colors.primary600};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const Preview = styled.img`
|
||||
width: ${({ theme }) => theme.spaces[10]};
|
||||
height: ${({ theme }) => theme.spaces[8]};
|
||||
/* Same overlay used in ModalLayout */
|
||||
background: ${({ theme }) => `${theme.colors.neutral800}1F`};
|
||||
border-radius: ${({ theme }) => theme.borderRadius};
|
||||
`;
|
||||
|
||||
const TypographyLineHeight = styled<TypographyComponent>(Typography)`
|
||||
/* line height of label and watch more to 1 so they can be better aligned visually */
|
||||
line-height: 1;
|
||||
`;
|
||||
|
||||
const TextLink = styled<TypographyComponent<'a'>>(Typography)`
|
||||
text-decoration: none;
|
||||
line-height: 1;
|
||||
|
||||
:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
|
||||
const VIDEO_LINKS = [
|
||||
{
|
||||
label: {
|
||||
id: 'app.components.Onboarding.link.build-content',
|
||||
defaultMessage: 'Build a content architecture',
|
||||
},
|
||||
href: 'https://www.youtube.com/watch?v=G9GjN0RxhkE',
|
||||
duration: '5:48',
|
||||
},
|
||||
{
|
||||
label: {
|
||||
id: 'app.components.Onboarding.link.manage-content',
|
||||
defaultMessage: 'Add & manage content',
|
||||
},
|
||||
href: 'https://www.youtube.com/watch?v=DEZw4KbybAI',
|
||||
duration: '3:18',
|
||||
},
|
||||
{
|
||||
label: { id: 'app.components.Onboarding.link.manage-media', defaultMessage: 'Manage media' },
|
||||
href: 'https://www.youtube.com/watch?v=-61MuiMQb38',
|
||||
duration: '3:41',
|
||||
},
|
||||
];
|
||||
|
||||
const WATCH_MORE = {
|
||||
href: 'https://www.youtube.com/playlist?list=PL7Q0DQYATmvidz6lEmwE5nIcOAYagxWqq',
|
||||
label: {
|
||||
id: 'app.components.Onboarding.link.more-videos',
|
||||
defaultMessage: 'Watch more videos',
|
||||
},
|
||||
};
|
||||
|
||||
const DOCUMENTATION_LINKS = [
|
||||
{
|
||||
label: { id: 'global.documentation', defaultMessage: 'documentation' },
|
||||
href: 'https://docs.strapi.io',
|
||||
icon: Book,
|
||||
},
|
||||
{
|
||||
label: { id: 'app.static.links.cheatsheet', defaultMessage: 'cheatsheet' },
|
||||
href: 'https://strapi-showcase.s3-us-west-2.amazonaws.com/CheatSheet.pdf',
|
||||
icon: PaperPlane,
|
||||
},
|
||||
];
|
||||
|
||||
export { Onboarding };
|
||||
@ -59,7 +59,6 @@ const Providers = ({ children, strapi, store }: ProvidersProps) => {
|
||||
<ConfigurationProvider
|
||||
defaultAuthLogo={strapi.configurations.authLogo}
|
||||
defaultMenuLogo={strapi.configurations.menuLogo}
|
||||
showTutorials={strapi.configurations.tutorials}
|
||||
showReleaseNotification={strapi.configurations.notifications.releases}
|
||||
>
|
||||
{children}
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
import { render } from '@tests/utils';
|
||||
|
||||
import { useAppInfo } from '../../features/AppInfo';
|
||||
import { Onboarding } from '../Onboarding';
|
||||
|
||||
jest.mock('../../features/AppInfo', () => ({
|
||||
...jest.requireActual('../../features/AppInfo'),
|
||||
useAppInfo: jest.fn((name, getter) => getter({ communityEdition: true })),
|
||||
}));
|
||||
|
||||
describe('Onboarding', () => {
|
||||
test.each([
|
||||
'watch more videos',
|
||||
'build a content architecture',
|
||||
'add & manage content',
|
||||
'manage media',
|
||||
'documentation',
|
||||
'cheatsheet',
|
||||
'get help',
|
||||
])('should display %s link', async (link) => {
|
||||
const { getByRole, user } = render(<Onboarding />);
|
||||
|
||||
await user.click(getByRole('button', { name: /open help menu/i }));
|
||||
|
||||
expect(getByRole('link', { name: new RegExp(link, 'i') })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should display discord link for CE edition', async () => {
|
||||
const { getByRole, user } = render(<Onboarding />);
|
||||
|
||||
await user.click(getByRole('button', { name: /open help menu/i }));
|
||||
|
||||
expect(getByRole('link', { name: /get help/i })).toHaveAttribute(
|
||||
'href',
|
||||
'https://discord.strapi.io'
|
||||
);
|
||||
});
|
||||
|
||||
test('should display support link for EE edition', async () => {
|
||||
// @ts-expect-error - mock
|
||||
useAppInfo.mockImplementation((name, getter) => getter({ communityEdition: false }));
|
||||
const { getByRole, user } = render(<Onboarding />);
|
||||
|
||||
await user.click(getByRole('button', { name: /open help menu/i }));
|
||||
|
||||
expect(getByRole('link', { name: /get help/i })).toHaveAttribute(
|
||||
'href',
|
||||
'https://support.strapi.io/support/home'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -43,7 +43,6 @@ interface ConfigurationContextValue {
|
||||
auth: ConfigurationLogo;
|
||||
menu: ConfigurationLogo;
|
||||
};
|
||||
showTutorials: boolean;
|
||||
showReleaseNotification: boolean;
|
||||
updateProjectSettings: (body: UpdateProjectSettingsBody) => Promise<void>;
|
||||
}
|
||||
@ -60,7 +59,6 @@ interface ConfigurationProviderProps {
|
||||
defaultAuthLogo: StrapiApp['configurations']['authLogo'];
|
||||
defaultMenuLogo: StrapiApp['configurations']['menuLogo'];
|
||||
showReleaseNotification?: boolean;
|
||||
showTutorials?: boolean;
|
||||
}
|
||||
|
||||
const ConfigurationProvider = ({
|
||||
@ -68,7 +66,6 @@ const ConfigurationProvider = ({
|
||||
defaultAuthLogo,
|
||||
defaultMenuLogo,
|
||||
showReleaseNotification = false,
|
||||
showTutorials = false,
|
||||
}: ConfigurationProviderProps) => {
|
||||
const { trackUsage } = useTracking();
|
||||
const { formatMessage } = useIntl();
|
||||
@ -160,7 +157,6 @@ const ConfigurationProvider = ({
|
||||
return (
|
||||
<ConfigurationContextProvider
|
||||
showReleaseNotification={showReleaseNotification}
|
||||
showTutorials={showTutorials}
|
||||
logos={{
|
||||
menu: {
|
||||
custom: isSuccess
|
||||
|
||||
@ -41,7 +41,6 @@ describe('ConfigurationProvider', () => {
|
||||
defaultAuthLogo={'strapi.jpg'}
|
||||
defaultMenuLogo={'strapi.jpg'}
|
||||
showReleaseNotification={false}
|
||||
showTutorials={false}
|
||||
>
|
||||
<TestComponent />
|
||||
</ConfigurationProvider>
|
||||
@ -58,7 +57,6 @@ describe('ConfigurationProvider', () => {
|
||||
defaultAuthLogo={'strapi.jpg'}
|
||||
defaultMenuLogo={'strapi.jpg'}
|
||||
showReleaseNotification={false}
|
||||
showTutorials={false}
|
||||
>
|
||||
<TestComponent />
|
||||
</ConfigurationProvider>
|
||||
|
||||
@ -13,7 +13,6 @@ import { GuidedTourModal } from '../components/GuidedTour/Modal';
|
||||
import { useGuidedTour } from '../components/GuidedTour/Provider';
|
||||
import { LeftMenu } from '../components/LeftMenu';
|
||||
import { NpsSurvey } from '../components/NpsSurvey';
|
||||
import { Onboarding } from '../components/Onboarding';
|
||||
import { Page } from '../components/PageHelpers';
|
||||
import { PluginsInitializer } from '../components/PluginsInitializer';
|
||||
import { PrivateRoute } from '../components/PrivateRoute';
|
||||
@ -91,7 +90,6 @@ const AdminLayout = () => {
|
||||
generalSectionLinks,
|
||||
pluginsSectionLinks,
|
||||
} = useMenu(checkLatestStrapiVersion(strapiVersion, tagName));
|
||||
const { showTutorials } = useConfiguration('Admin');
|
||||
|
||||
/**
|
||||
* Make sure the event is only send once after accessing the admin panel
|
||||
@ -130,7 +128,6 @@ const AdminLayout = () => {
|
||||
<Box flex={1}>
|
||||
<Outlet />
|
||||
<GuidedTourModal />
|
||||
{showTutorials && <Onboarding />}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
||||
@ -155,7 +155,6 @@ const Providers = ({ children, initialEntries, storeConfig, permissions = [] }:
|
||||
<GuidedTourProvider>
|
||||
<ConfigurationContextProvider
|
||||
showReleaseNotification={false}
|
||||
showTutorials={false}
|
||||
logos={{
|
||||
auth: { default: 'default' },
|
||||
menu: { default: 'default' },
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -44,7 +44,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -71,7 +71,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -8,6 +8,9 @@ import { previewAdmin } from './preview';
|
||||
import { routes } from './router';
|
||||
import { prefixPluginTranslations } from './utils/translations';
|
||||
|
||||
// NOTE: we have to preload it to ensure chunks will have it available as global
|
||||
import 'prismjs';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default {
|
||||
register(app: any) {
|
||||
|
||||
@ -13,9 +13,57 @@ import { codeLanguages } from '../utils/constants';
|
||||
import { baseHandleConvert } from '../utils/conversions';
|
||||
import { pressEnterTwiceToExit } from '../utils/enterKey';
|
||||
import { type Block } from '../utils/types';
|
||||
// Import the PrismJS theme to highlight the code
|
||||
|
||||
import 'prismjs/themes/prism-solarizedlight.css';
|
||||
import './utils/prismLanguages';
|
||||
import 'prismjs/components/prism-asmatmel';
|
||||
import 'prismjs/components/prism-bash';
|
||||
import 'prismjs/components/prism-basic';
|
||||
import 'prismjs/components/prism-c';
|
||||
import 'prismjs/components/prism-clojure';
|
||||
import 'prismjs/components/prism-cobol';
|
||||
import 'prismjs/components/prism-cpp';
|
||||
import 'prismjs/components/prism-csharp';
|
||||
import 'prismjs/components/prism-dart';
|
||||
import 'prismjs/components/prism-docker';
|
||||
import 'prismjs/components/prism-elixir';
|
||||
import 'prismjs/components/prism-erlang';
|
||||
import 'prismjs/components/prism-fortran';
|
||||
import 'prismjs/components/prism-fsharp';
|
||||
import 'prismjs/components/prism-go';
|
||||
import 'prismjs/components/prism-graphql';
|
||||
import 'prismjs/components/prism-groovy';
|
||||
import 'prismjs/components/prism-haskell';
|
||||
import 'prismjs/components/prism-haxe';
|
||||
import 'prismjs/components/prism-ini';
|
||||
import 'prismjs/components/prism-java';
|
||||
import 'prismjs/components/prism-javascript';
|
||||
import 'prismjs/components/prism-jsx';
|
||||
import 'prismjs/components/prism-json';
|
||||
import 'prismjs/components/prism-julia';
|
||||
import 'prismjs/components/prism-kotlin';
|
||||
import 'prismjs/components/prism-latex';
|
||||
import 'prismjs/components/prism-lua';
|
||||
import 'prismjs/components/prism-markdown';
|
||||
import 'prismjs/components/prism-matlab';
|
||||
import 'prismjs/components/prism-makefile';
|
||||
import 'prismjs/components/prism-objectivec';
|
||||
import 'prismjs/components/prism-perl';
|
||||
import 'prismjs/components/prism-php';
|
||||
import 'prismjs/components/prism-powershell';
|
||||
import 'prismjs/components/prism-python';
|
||||
import 'prismjs/components/prism-r';
|
||||
import 'prismjs/components/prism-ruby';
|
||||
import 'prismjs/components/prism-rust';
|
||||
import 'prismjs/components/prism-sas';
|
||||
import 'prismjs/components/prism-scala';
|
||||
import 'prismjs/components/prism-scheme';
|
||||
import 'prismjs/components/prism-sql';
|
||||
import 'prismjs/components/prism-stata';
|
||||
import 'prismjs/components/prism-swift';
|
||||
import 'prismjs/components/prism-typescript';
|
||||
import 'prismjs/components/prism-tsx';
|
||||
import 'prismjs/components/prism-vbnet';
|
||||
import 'prismjs/components/prism-yaml';
|
||||
|
||||
type BaseRangeCustom = BaseRange & { className: string };
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { BaseLink, Button, Field, Flex, Popover, useComposedRefs } from '@strapi/design-system';
|
||||
import { Box, Button, Field, Flex, Popover, useComposedRefs } from '@strapi/design-system';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { Editor, Path, Range, Transforms } from 'slate';
|
||||
import { type RenderElementProps, ReactEditor } from 'slate-react';
|
||||
@ -10,7 +10,7 @@ import { type BlocksStore, useBlocksEditorContext } from '../BlocksEditor';
|
||||
import { editLink, removeLink } from '../utils/links';
|
||||
import { isLinkNode, type Block } from '../utils/types';
|
||||
|
||||
const StyledBaseLink = styled(BaseLink)`
|
||||
const StyledLink = styled(Box)`
|
||||
text-decoration: none;
|
||||
`;
|
||||
|
||||
@ -90,15 +90,16 @@ const LinkContent = React.forwardRef<HTMLAnchorElement, LinkContentProps>(
|
||||
return (
|
||||
<Popover.Root open={popoverOpen}>
|
||||
<Popover.Trigger>
|
||||
<StyledBaseLink
|
||||
<StyledLink
|
||||
{...attributes}
|
||||
ref={forwardedRef}
|
||||
tag="a"
|
||||
href={link.url}
|
||||
onClick={() => setPopoverOpen(true)}
|
||||
color="primary600"
|
||||
>
|
||||
{children}
|
||||
</StyledBaseLink>
|
||||
</StyledLink>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content onPointerDownOutside={handleClose}>
|
||||
<Flex padding={4} direction="column" gap={4}>
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
import 'prismjs/components/prism-asmatmel';
|
||||
import 'prismjs/components/prism-bash';
|
||||
import 'prismjs/components/prism-basic';
|
||||
import 'prismjs/components/prism-c';
|
||||
import 'prismjs/components/prism-clojure';
|
||||
import 'prismjs/components/prism-cobol';
|
||||
import 'prismjs/components/prism-cpp';
|
||||
import 'prismjs/components/prism-csharp';
|
||||
import 'prismjs/components/prism-dart';
|
||||
import 'prismjs/components/prism-docker';
|
||||
import 'prismjs/components/prism-elixir';
|
||||
import 'prismjs/components/prism-erlang';
|
||||
import 'prismjs/components/prism-fortran';
|
||||
import 'prismjs/components/prism-fsharp';
|
||||
import 'prismjs/components/prism-go';
|
||||
import 'prismjs/components/prism-graphql';
|
||||
import 'prismjs/components/prism-groovy';
|
||||
import 'prismjs/components/prism-haskell';
|
||||
import 'prismjs/components/prism-haxe';
|
||||
import 'prismjs/components/prism-ini';
|
||||
import 'prismjs/components/prism-java';
|
||||
import 'prismjs/components/prism-javascript';
|
||||
import 'prismjs/components/prism-jsx';
|
||||
import 'prismjs/components/prism-json';
|
||||
import 'prismjs/components/prism-julia';
|
||||
import 'prismjs/components/prism-kotlin';
|
||||
import 'prismjs/components/prism-latex';
|
||||
import 'prismjs/components/prism-lua';
|
||||
import 'prismjs/components/prism-markdown';
|
||||
import 'prismjs/components/prism-matlab';
|
||||
import 'prismjs/components/prism-makefile';
|
||||
import 'prismjs/components/prism-objectivec';
|
||||
import 'prismjs/components/prism-perl';
|
||||
import 'prismjs/components/prism-php';
|
||||
import 'prismjs/components/prism-powershell';
|
||||
import 'prismjs/components/prism-python';
|
||||
import 'prismjs/components/prism-r';
|
||||
import 'prismjs/components/prism-ruby';
|
||||
import 'prismjs/components/prism-rust';
|
||||
import 'prismjs/components/prism-sas';
|
||||
import 'prismjs/components/prism-scala';
|
||||
import 'prismjs/components/prism-scheme';
|
||||
import 'prismjs/components/prism-sql';
|
||||
import 'prismjs/components/prism-stata';
|
||||
import 'prismjs/components/prism-swift';
|
||||
import 'prismjs/components/prism-typescript';
|
||||
import 'prismjs/components/prism-tsx';
|
||||
import 'prismjs/components/prism-vbnet';
|
||||
import 'prismjs/components/prism-yaml';
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'named',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -64,7 +64,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/shared'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -306,6 +306,7 @@ const DataManagerProvider = ({ children }: DataManagerProviderProps) => {
|
||||
try {
|
||||
const requestURL = `/${pluginId}/${endPoint}/${currentUid}`;
|
||||
const isTemporary = get(modifiedData, [firstKeyToMainSchema, 'isTemporary'], false);
|
||||
|
||||
// eslint-disable-next-line no-alert
|
||||
const userConfirm = window.confirm(
|
||||
formatMessage({
|
||||
@ -339,6 +340,7 @@ const DataManagerProvider = ({ children }: DataManagerProviderProps) => {
|
||||
// Unlock the app
|
||||
await unlockAppWithAutoreload?.();
|
||||
|
||||
await getDataRef.current();
|
||||
// Refetch the permissions
|
||||
await updatePermissions();
|
||||
}
|
||||
@ -562,7 +564,6 @@ const DataManagerProvider = ({ children }: DataManagerProviderProps) => {
|
||||
|
||||
// refetch and update initial state after the data has been saved
|
||||
await getDataRef.current();
|
||||
dispatch(actions.updateInitialState());
|
||||
|
||||
// Update the app's permissions
|
||||
await updatePermissions();
|
||||
|
||||
@ -160,6 +160,35 @@ const slice = createSlice({
|
||||
state.contentTypes = contentTypes;
|
||||
state.reservedNames = reservedNames;
|
||||
state.isLoading = false;
|
||||
|
||||
state.modifiedData = {
|
||||
...DEFAULT_MODIFIED_DATA,
|
||||
component: state.modifiedData.component
|
||||
? components[state.modifiedData.component.uid]
|
||||
: undefined,
|
||||
contentType: state.modifiedData.contentType
|
||||
? contentTypes[state.modifiedData.contentType.uid]
|
||||
: undefined,
|
||||
components: state.modifiedData.components
|
||||
? Object.keys(state.modifiedData.components).reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = components[key];
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, Component>
|
||||
)
|
||||
: {},
|
||||
contentTypes: state.modifiedData.contentTypes
|
||||
? Object.keys(state.modifiedData.contentTypes).reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = contentTypes[key];
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, ContentType>
|
||||
)
|
||||
: {},
|
||||
};
|
||||
state.initialData = state.modifiedData;
|
||||
},
|
||||
addAttribute: (state, action: PayloadAction<AddAttributePayload>) => {
|
||||
const { attributeToSet, forTarget, targetUid, shouldAddComponentToData } = action.payload;
|
||||
|
||||
@ -30,7 +30,7 @@ export interface Component {
|
||||
}
|
||||
|
||||
export interface ContentType {
|
||||
uid?: Internal.UID.ContentType;
|
||||
uid: Internal.UID.ContentType;
|
||||
isTemporary?: boolean;
|
||||
visible?: boolean;
|
||||
name?: string;
|
||||
|
||||
@ -47,7 +47,9 @@ export const formsAPI: any = {
|
||||
extendContentType({ validator, form: { advanced, base } }: any) {
|
||||
const { contentType } = this.types;
|
||||
|
||||
contentType.validators.push(validator);
|
||||
if (validator) {
|
||||
contentType.validators.push(validator);
|
||||
}
|
||||
contentType.form.advanced.push(advanced);
|
||||
contentType.form.base.push(base);
|
||||
},
|
||||
@ -69,7 +71,9 @@ export const formsAPI: any = {
|
||||
};
|
||||
}
|
||||
|
||||
formType[field].validators.push(validator);
|
||||
if (validator) {
|
||||
formType[field].validators.push(validator);
|
||||
}
|
||||
formType[field].form.advanced.push(advanced);
|
||||
formType[field].form.base.push(base);
|
||||
});
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -23,7 +23,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: import.meta.dirname + '/dist',
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -17,7 +17,7 @@ export default defineConfig({
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -18,7 +18,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/admin'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
@ -41,7 +41,7 @@ export default defineConfig([
|
||||
{
|
||||
dir: path.join(import.meta.dirname, 'dist/server'),
|
||||
entryFileNames: '[name].mjs',
|
||||
chunkFileNames: 'chunks/[name]-[hash].js',
|
||||
chunkFileNames: 'chunks/[name]-[hash].mjs',
|
||||
exports: 'auto',
|
||||
format: 'esm',
|
||||
sourcemap: true,
|
||||
|
||||
@ -28,11 +28,9 @@ const goToHistoryPage = async (page: Page) => {
|
||||
await moreActionsButton.click();
|
||||
const historyButton = page.getByRole('menuitem', { name: /content history/i });
|
||||
|
||||
// TODO find out why the history button sometimes doesn't appear. Reloading shouldn't be necessary
|
||||
if (await historyButton.isVisible()) {
|
||||
await clickAndWait(page, historyButton);
|
||||
} else {
|
||||
await page.reload();
|
||||
await goToHistoryPage(page);
|
||||
}
|
||||
};
|
||||
|
||||
@ -41,8 +41,6 @@ test.describe('Edit collection type', () => {
|
||||
await page.getByRole('button', { name: 'Finish' }).click();
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
// TODO: this is here because of a bug where the admin UI doesn't understand the option has changed
|
||||
// Fix the bug then remove this
|
||||
await waitForRestart(page);
|
||||
|
||||
await expect(page.getByRole('cell', { name: 'product', exact: true })).toBeVisible();
|
||||
@ -66,10 +64,6 @@ test.describe('Edit collection type', () => {
|
||||
await waitForRestart(page);
|
||||
await expect(page.getByRole('heading', { name: ctName })).toBeVisible();
|
||||
|
||||
// TODO: this is here because of a bug where the admin UI doesn't understand the option has changed
|
||||
// Fix the bug then remove this
|
||||
await page.reload();
|
||||
|
||||
// toggle on - we see that the "off" worked because here it doesn't prompt to confirm data loss
|
||||
await page.getByRole('button', { name: 'Edit' }).first().click();
|
||||
await page.getByRole('tab', { name: 'Advanced settings' }).click();
|
||||
@ -89,10 +83,6 @@ test.describe('Edit collection type', () => {
|
||||
await waitForRestart(page);
|
||||
await expect(page.getByRole('heading', { name: ctName })).toBeVisible();
|
||||
|
||||
// TODO: this is here because of a bug where the admin UI doesn't understand the option has changed
|
||||
// Fix the bug then remove this
|
||||
await page.reload();
|
||||
|
||||
// toggle on - we see that the "off" worked because here it doesn't prompt to confirm data loss
|
||||
await page.getByRole('button', { name: 'Edit' }).first().click();
|
||||
await page.getByRole('tab', { name: 'Advanced settings' }).click();
|
||||
@ -176,9 +166,6 @@ test.describe('Edit collection type', () => {
|
||||
|
||||
await waitForRestart(page);
|
||||
|
||||
// TODO: fix bug that requires a page refresh to see that content types have been updated
|
||||
await page.reload();
|
||||
|
||||
await expect(page.getByRole('heading', { name: newname })).toBeVisible();
|
||||
});
|
||||
|
||||
@ -192,9 +179,6 @@ test.describe('Edit collection type', () => {
|
||||
|
||||
await waitForRestart(page);
|
||||
|
||||
// TODO: fix bug that requires a page refresh to see that content types have been updated
|
||||
await page.reload();
|
||||
|
||||
await expect(page.getByRole('heading', { name: ctName })).not.toBeVisible();
|
||||
});
|
||||
|
||||
|
||||
@ -137,9 +137,6 @@ test.describe('Update a new component', () => {
|
||||
// delete it
|
||||
await deleteComponent(page, 'SomeComponent');
|
||||
|
||||
// TODO: fix issue that components aren't removed from side navigation or content types until refresh
|
||||
await page.reload({ waitUntil: 'networkidle' });
|
||||
|
||||
// confirm that it no longer exists in the content type this component was in
|
||||
await navToHeader(page, ['Content-Type Builder', collectionType.name], collectionType.name);
|
||||
await expect(page.getByText(componentAttributeName, { exact: true })).not.toBeVisible();
|
||||
|
||||
@ -40,10 +40,6 @@ test.describe('Edit single type', () => {
|
||||
await waitForRestart(page);
|
||||
await expect(page.getByRole('heading', { name: ctName })).toBeVisible();
|
||||
|
||||
// TODO: this is here because of a bug where the admin UI doesn't understand the option has changed
|
||||
// Fix the bug then remove this
|
||||
await page.reload();
|
||||
|
||||
// toggle on - we see that the "off" worked because here it doesn't prompt to confirm data loss
|
||||
await page.getByRole('button', { name: 'Edit', exact: true }).click();
|
||||
await page.getByRole('tab', { name: 'Advanced settings' }).click();
|
||||
@ -63,10 +59,6 @@ test.describe('Edit single type', () => {
|
||||
await waitForRestart(page);
|
||||
await expect(page.getByRole('heading', { name: ctName })).toBeVisible();
|
||||
|
||||
// TODO: this is here because of a bug where the admin UI doesn't understand the option has changed
|
||||
// Fix the bug then remove this
|
||||
await page.reload();
|
||||
|
||||
// toggle on - we see that the "off" worked because here it doesn't prompt to confirm data loss
|
||||
await page.getByRole('button', { name: 'Edit', exact: true }).click();
|
||||
await page.getByRole('tab', { name: 'Advanced settings' }).click();
|
||||
@ -102,9 +94,6 @@ test.describe('Edit single type', () => {
|
||||
|
||||
await waitForRestart(page);
|
||||
|
||||
// TODO: fix bug that requires a page refresh to see that content types have been updated
|
||||
await page.reload();
|
||||
|
||||
await expect(page.getByRole('heading', { name: newname })).toBeVisible();
|
||||
});
|
||||
|
||||
@ -118,9 +107,6 @@ test.describe('Edit single type', () => {
|
||||
|
||||
await waitForRestart(page);
|
||||
|
||||
// TODO: fix bug that requires a page refresh to see that content types have been updated
|
||||
await page.reload();
|
||||
|
||||
await expect(page.getByRole('heading', { name: ctName })).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user