Merge pull request #10881 from strapi/upgrade-plan

UpgradePlanModal
This commit is contained in:
cyril lopez 2021-09-07 08:20:38 +02:00 committed by GitHub
commit 4be4b3ef01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 278 additions and 180 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -0,0 +1,126 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { useIntl } from 'react-intl';
import { Portal } from '@strapi/parts/Portal';
import { FocusTrap } from '@strapi/parts/FocusTrap';
import { IconButton } from '@strapi/parts/IconButton';
import { LinkButton } from '@strapi/parts/LinkButton';
import { Box } from '@strapi/parts/Box';
import { Row } from '@strapi/parts/Row';
import { Text, H1 } from '@strapi/parts/Text';
import { Stack } from '@strapi/parts/Stack';
import ExternalLink from '@strapi/icons/ExternalLink';
import CloseAlertIcon from '@strapi/icons/CloseAlertIcon';
import { setHexOpacity, useLockScroll } from '@strapi/helper-plugin';
import AirBalloon from '../../assets/images/hot-air-balloon.png';
import BigArrow from '../../assets/images/upgrade-details.png';
const UpgradeWrapper = styled.div`
position: absolute;
z-index: 3;
inset: 0;
background: ${({ theme }) => setHexOpacity(theme.colors.neutral800, 0.2)};
padding: 0 ${({ theme }) => theme.spaces[8]};
`;
const UpgradeContainer = styled(Row)`
position: relative;
max-width: ${830 / 16}rem;
height: ${415 / 16}rem;
margin: 0 auto;
overflow: hidden;
margin-top: 10%;
padding-left: ${64 / 16}rem;
img:first-of-type {
position: absolute;
right: 0;
top: 0;
max-width: ${360 / 16}rem;
}
img:not(:first-of-type) {
width: ${130 / 16}rem;
margin-left: 12%;
margin-right: ${20 / 16}rem;
z-index: 0;
}
`;
const TextBold = styled(Text)`
font-weight: 700;
`;
const StackFlexStart = styled(Stack)`
align-items: flex-start;
max-width: ${400 / 16}rem;
z-index: 0;
`;
const CloseButtonContainer = styled(Box)`
position: absolute;
right: ${({ theme }) => theme.spaces[4]};
top: ${({ theme }) => theme.spaces[4]};
`;
const UpgradePlanModal = ({ onClose, isOpen }) => {
useLockScroll(isOpen);
const { formatMessage } = useIntl();
if (!isOpen) {
return null;
}
return (
<Portal>
<UpgradeWrapper>
<FocusTrap onClose={onClose}>
<UpgradeContainer aria-labelledby="upgrade-plan" background="neutral0" hasRadius>
<img src={AirBalloon} alt="air-balloon" />
<CloseButtonContainer>
<IconButton onClick={onClose} label="Close" icon={<CloseAlertIcon />} />
</CloseButtonContainer>
<StackFlexStart size={6}>
<TextBold textColor="primary600">
{formatMessage({
id: 'app.components.UpgradePlanModal.text-ce',
defaultMessage: 'COMMUNITY EDITION',
})}
</TextBold>
<Stack size={2}>
<H1 as="h2" id="upgrade-plan">
{formatMessage({
id: 'app.components.UpgradePlanModal.limit-reached',
defaultMessage: 'You have reached the limit',
})}
</H1>
<Text>
{formatMessage({
id: 'app.components.UpgradePlanModal.text-power',
defaultMessage:
'Unlock the full power of Strapi by upgrading your plan to the Enterprise Edition',
})}
</Text>
</Stack>
<LinkButton href="https://strapi.io/pricing-self-hosted" endIcon={<ExternalLink />}>
{formatMessage({
id: 'app.components.UpgradePlanModal.button',
defaultMessage: 'Learn more',
})}
</LinkButton>
</StackFlexStart>
<img src={BigArrow} alt="upgrade-arrow" />
</UpgradeContainer>
</FocusTrap>
</UpgradeWrapper>
</Portal>
);
};
UpgradePlanModal.propTypes = {
onClose: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
};
export default UpgradePlanModal;

View File

@ -0,0 +1,63 @@
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { IntlProvider } from 'react-intl';
import { ThemeProvider, lightTheme } from '@strapi/parts';
import UpgradePlanModal from '../index';
const App = (
<ThemeProvider theme={lightTheme}>
<IntlProvider locale="en" messages={{ en: {} }} textComponent="span">
<UpgradePlanModal isOpen onClose={jest.fn()} />
</IntlProvider>
</ThemeProvider>
);
describe('UpgradePlanModal', () => {
it('renders and matches the snapshot', async () => {
const {
container: { firstChild },
} = render(App);
expect(firstChild).toMatchInlineSnapshot(`
.c0 {
border: 0;
-webkit-clip: rect(0 0 0 0);
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
}
<div
class="c0"
>
<p
aria-live="polite"
id="live-region-log"
role="log"
/>
<p
aria-live="polite"
id="live-region-status"
role="status"
/>
<p
aria-live="assertive"
id="live-region-alert"
role="alert"
/>
</div>
`);
});
it('renders and matches the snapshot', async () => {
render(App);
await waitFor(() => {
expect(screen.getByText('You have reached the limit')).toBeInTheDocument();
});
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,16 +0,0 @@
import styled from 'styled-components';
import logo from './arrow.png';
const Img = styled.img`
position: absolute;
top: 130px;
right: 195px;
height: 82px;
`;
Img.defaultProps = {
alt: 'arrow',
src: logo,
};
export default Img;

View File

@ -1,13 +0,0 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import styled from 'styled-components';
const Download = styled(FontAwesomeIcon)`
margin-left: 10px;
transform: rotate(-45deg);
`;
Download.defaultProps = {
icon: 'arrow-right',
};
export default Download;

View File

@ -1,12 +0,0 @@
import styled from 'styled-components';
const Wrapper = styled.div`
height: 25px;
max-width: fit-content;
padding: 0 15px;
background: rgba(0, 126, 255, 0.08);
border: 1px solid rgba(0, 126, 255, 0.24);
border-radius: 2px;
`;
export default Wrapper;

View File

@ -1,18 +0,0 @@
import React from 'react';
import { Text } from '@buffetjs/core';
import { useIntl } from 'react-intl';
import Wrapper from './Wrapper';
const Option = () => {
const { formatMessage } = useIntl();
return (
<Wrapper left right size="xs">
<Text color="mediumBlue" lineHeight="23px">
{formatMessage({ id: 'app.components.UpgradePlanModal.text-ce' })}
</Text>
</Wrapper>
);
};
export default Option;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 423 KiB

View File

@ -1,14 +0,0 @@
import styled from 'styled-components';
import Balloon from './balloon.png';
const Wrapper = styled.div`
height: 390px;
width: 100%;
padding-top: 89px;
padding-left: 65px;
background-image: url(${Balloon});
background-size: contain;
background-position: right;
`;
export default Wrapper;

View File

@ -1,4 +0,0 @@
export { default as Arrow } from './Arrow';
export { default as Download } from './Download';
export { default as Option } from './Option';
export { default as Wrapper } from './Wrapper';

View File

@ -1,91 +0,0 @@
/* eslint-disable jsx-a11y/anchor-has-content */
/* eslint-disable jsx-a11y/control-has-associated-label */
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import { Button, Padded, Text } from '@buffetjs/core';
import { Modal } from '@strapi/helper-plugin';
import { useIntl } from 'react-intl';
import { Arrow, Download, Option, Wrapper } from './components';
const UpgradePlanModal = ({ isOpen, onToggle }) => {
const ref = useRef();
const { formatMessage } = useIntl();
const handleClick = () => {
ref.current.click();
};
return (
<Modal isOpen={isOpen} onToggle={onToggle} closeButtonColor="#fff">
<Wrapper>
<Padded>
<Option />
<Padded top size="smd">
<Padded top size="xs">
<Text fontSize="xl" fontWeight="bold" lineHeight="24px">
{formatMessage({
id: 'app.components.UpgradePlanModal.limit-reached',
defaultMessage: 'You have reached the limit',
})}
</Text>
</Padded>
</Padded>
<Padded style={{ maxWidth: 405 }} top size="smd">
<Text color="black" lineHeight="18px">
<Text as="span" fontSize="md" fontWeight="semiBold">
{formatMessage({
id: 'app.components.UpgradePlanModal.text-power',
defaultMessage: 'Unlock the full power',
})}
</Text>
&nbsp;
<Text as="span" fontSize="md">
{formatMessage({
id: 'app.components.UpgradePlanModal.text-strapi',
defaultMessage: 'of Strapi by upgrading your plan to the',
})}
</Text>
<br />
<Text as="span" fontSize="md" fontWeight="semiBold">
{formatMessage({
id: 'app.components.UpgradePlanModal.text-ee',
defaultMessage: 'Enterprise Edititon',
})}
</Text>
</Text>
</Padded>
<Padded top size="md">
<Button color="primary" onClick={handleClick} style={{ paddingRight: 0 }}>
{formatMessage({
id: 'app.components.UpgradePlanModal.button',
defaultMessage: 'LEARN MORE',
})}
<Download />
</Button>
</Padded>
</Padded>
<Arrow />
</Wrapper>
<a
href="https://strapi.io/pricing"
target="_blank"
rel="noopener noreferrer"
style={{ display: 'none' }}
ref={ref}
/>
</Modal>
);
};
UpgradePlanModal.defaultProps = {
isOpen: false,
onToggle: () => {},
};
UpgradePlanModal.propTypes = {
isOpen: PropTypes.bool,
onToggle: PropTypes.func,
};
export default UpgradePlanModal;

View File

@ -186,7 +186,7 @@ const RoleListPage = () => {
</Table>
{!rowCount && !isLoading && <EmptyRole />}
</ContentLayout>
<UpgradePlanModal isOpen={isModalOpen} onToggle={handleToggle} />
<UpgradePlanModal isOpen={isModalOpen} onClose={handleToggle} />
</Main>
);
};

View File

@ -400,10 +400,6 @@ describe('<ListPage />', () => {
border: none;
}
.c37 {
font-family: Lato;
}
<main
aria-labelledby="title"
class="c0"
@ -612,9 +608,6 @@ describe('<ListPage />', () => {
</div>
</div>
</div>
<div
class="c37"
/>
</main>
`);
});

View File

@ -279,11 +279,11 @@
"app.components.PluginCard.compatible": "Compatible with your app",
"app.components.PluginCard.compatibleCommunity": "Compatible with the community",
"app.components.PluginCard.more-details": "More details",
"app.components.UpgradePlanModal.button": "LEARN MORE",
"app.components.UpgradePlanModal.button": "Learn more",
"app.components.UpgradePlanModal.limit-reached": "You have reached the limit",
"app.components.UpgradePlanModal.text-ce": "Community Edition",
"app.components.UpgradePlanModal.text-ce": "COMMUNITY EDITION",
"app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
"app.components.UpgradePlanModal.text-power": "Unlock the full power",
"app.components.UpgradePlanModal.text-power": "Unlock the full power of Strapi by upgrading your plan to the Enterprise Edition",
"app.components.UpgradePlanModal.text-strapi": "of Strapi by upgrading your plan to the",
"app.components.Users.MagicLink.connect": "Send this link to the user for them to connect.",
"app.components.Users.MagicLink.connect.sso": "Send this link to the user, the first login can be made via a SSO provider",

View File

@ -0,0 +1,15 @@
import { useEffect } from 'react';
const useLockScroll = lockScroll => {
useEffect(() => {
if (lockScroll) {
document.body.classList.add('lock-body-scroll');
}
return () => {
document.body.classList.remove('lock-body-scroll');
};
}, [lockScroll]);
};
export default useLockScroll;

View File

@ -0,0 +1,36 @@
<!--- useLockScroll.stories.mdx --->
import { Meta } from '@storybook/addon-docs';
<Meta title="hooks/useLockScroll" />
# useLockScroll
This hook is used in order to display a notification in the admin panel.
## Usage
```
import { useLockScroll } from '@strapi/helper-plugin';
const Modal = ({ onToggle, isOpen }) => {
// boolean required to activate locked scroll when modal is open
useLockScroll(isOpen);
if (!isOpen) {
return null;
}
return (
<Portal>
<ModalWrapper>
<FocusTrap onClose={onToggle}>
<ModalContainer>
Content of the modal
</ModalContainer>
</FocusTrap>
</ModalWrapper>
</Portal>
);
};
```

View File

@ -127,6 +127,7 @@ export { default as useRBACProvider } from './hooks/useRBACProvider';
export { default as useRBAC } from './hooks/useRBAC';
export { default as usePersistentState } from './hooks/usePersistentState';
export { default as useFocusWhenNavigate } from './hooks/useFocusWhenNavigate';
export { default as useLockScroll } from './hooks/useLockScroll';
// Providers
export { default as LibraryProvider } from './providers/LibraryProvider';
@ -164,6 +165,9 @@ export { default as prefixPluginTranslations } from './old/utils/prefixPluginTra
export { default as pxToRem } from './utils/pxToRem';
export { default as to } from './utils/await-to-js';
// NEW UTILS
export { default as setHexOpacity } from './utils/setHexOpacity';
// SVGS
export { default as LayoutIcon } from './old/svgs/Layout';
export { default as ClearIcon } from './old/svgs/Clear';

View File

@ -0,0 +1,6 @@
const setHexOpacity = (hex, alpha) =>
`${hex}${Math.floor(alpha * 255)
.toString(16)
.padStart(2, 0)}`;
export default setHexOpacity;

View File

@ -0,0 +1,23 @@
<!--- setHexOpacity.stories.mdx --->
import { Meta } from '@storybook/addon-docs';
<Meta title="utils/setHexOpacity" />
# setHexOpacity
This hook is used in order to display a notification in the admin panel.
## Usage
```
import { setHexOpacity } from '@strapi/helper-plugin';
const BoxCustom = styled.div`
// using strapi theme hex
background: ${({ theme }) => setHexOpacity(theme.colors.neutral800, 0.2)};
// using hex
background: setHexOpacity('#12100E', 0.2)};
`;
```