Guided tour/stepper (#12082)

* feat: added guided tour context, hook and provider to helper-plugin

* feat(guided-tour): using helpers in the admin + init reducer

Co-authored-by: ronronscelestes <ronronscelestes@users.noreply.github.com>

* feedback fixes

* created data structure for guided tour state and content

Co-authored-by: Vincent <vincentbpro@users.noreply.github.com>

* removed closed leaf key from initialState

* init guided tour home component

* feedback fix fontWeight prop

* Stepper, StepNumber, StepLine, Step

* wip adding Stepper to homepage

* WIP: homepage stepper

Co-authored-by: ronronscelestes <ronronscelestes@users.noreply.github.com>

* fixed grid aligment StepHomepage

* feat: added homepage stepper

Co-authored-by: ronronscelestes <ronronscelestes@users.noreply.github.com>

* changed StepLine color if isNotDone + added useGuidedTour mock and updated snapshort for homepage tests

* removed guided tour component from homepage until we add local storage and user role detection in the next PR

* removed changed from homepage snapshots

Co-authored-by: ronronscelestes <ronronscelestes@users.noreply.github.com>
Co-authored-by: ronronscelestes <julie.plantey@gmail.com>
Co-authored-by: Vincent <vincentbpro@users.noreply.github.com>
This commit is contained in:
Vincent 2022-01-05 17:45:43 +01:00 committed by GitHub
parent ccd92c08ad
commit 81e1fb2de9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 257 additions and 14 deletions

View File

@ -1,35 +1,40 @@
import React from 'react';
import { useIntl } from 'react-intl';
import { useGuidedTour } from '@strapi/helper-plugin';
import { Box } from '@strapi/design-system/Box';
import { useIntl } from 'react-intl';
import { Stack } from '@strapi/design-system/Stack';
import { Typography } from '@strapi/design-system/Typography';
import { LinkButton } from '@strapi/design-system/LinkButton';
import ArrowRight from '@strapi/icons/ArrowRight';
import StepperHomepage from '../Stepper/Homepage/StepperHomepage';
import layout from '../layout';
const GuidedTourHomepage = () => {
const { formatMessage } = useIntl();
const { guidedTourState } = useGuidedTour();
const { formatMessage } = useIntl();
const sections = Object.entries(layout).map(([key, val]) => ({ key, ...val.home }));
const sections = Object.entries(layout).map(([key, val]) => ({
key,
title: val.home.title,
content: (
<LinkButton to={val.home.cta.target} endIcon={<ArrowRight />}>
{formatMessage(val.home.cta.title)}
</LinkButton>
),
}));
const enrichedSections = sections.map(section => ({
isDone: Object.entries(guidedTourState[section.key]).every(([, value]) => value),
...section,
}));
const activeSection = enrichedSections.find(section => !section.isDone).key;
const activeSection = enrichedSections.find(section => !section.isDone)?.key;
return (
<Stack size={5}>
{enrichedSections.map(section => (
<Box hasRadius shadow="tableShadow" background="neutral0" padding={5} key={section.key}>
<Typography>{formatMessage(section.title)}</Typography>
{section.key === activeSection && (
<LinkButton to={section.cta.target}>{formatMessage(section.cta.title)}</LinkButton>
)}
</Box>
))}
<Stack size={6} hasRadius shadow="tableShadow" padding={7} background="neutral0">
<Typography variant="beta" as="h2">
Guided tour
</Typography>
<StepperHomepage sections={sections} currentSectionKey={activeSection} />
</Stack>
);
};

View File

@ -0,0 +1,58 @@
import React from 'react';
import { useIntl } from 'react-intl';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { pxToRem } from '@strapi/helper-plugin';
import { Typography } from '@strapi/design-system/Typography';
import { Box } from '@strapi/design-system/Box';
import StepNumber from '../StepNumber';
import StepLine from '../StepLine';
const GridItemAlignCenter = styled(Box)`
align-self: center;
`;
const GridItemJustifyCenter = styled(Box)`
justify-self: center;
`;
const StepHomepage = ({ type, title, number, content, hasLine }) => {
const { formatMessage } = useIntl();
return (
<>
<Box>
<StepNumber type={type} number={number} />
</Box>
<GridItemAlignCenter>
<Typography variant="delta" as="h3">
{formatMessage(title)}
</Typography>
</GridItemAlignCenter>
<GridItemJustifyCenter height="100%">
{hasLine && <StepLine type={type} minHeight={pxToRem(64)} />}
</GridItemJustifyCenter>
<Box>{type === 'isActive' && content}</Box>
</>
);
};
StepHomepage.defaultProps = {
content: undefined,
number: undefined,
type: 'isNotDone',
hasLine: true,
};
StepHomepage.propTypes = {
content: PropTypes.node,
number: PropTypes.number,
title: PropTypes.shape({
id: PropTypes.string,
defaultMessage: PropTypes.string,
}).isRequired,
type: PropTypes.oneOf(['isActive', 'isDone', 'isNotDone']),
hasLine: PropTypes.bool,
};
export default StepHomepage;

View File

@ -0,0 +1,64 @@
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { pxToRem } from '@strapi/helper-plugin';
import { Grid } from '@strapi/design-system/Grid';
import StepHomepage from './StepHomepage';
const GridCustom = styled(Grid)`
gap: ${({ theme }) => `${theme.spaces[3]} ${theme.spaces[4]}`};
grid-template-columns: ${pxToRem(30)} 1fr;
`;
const getType = (activeSectionIndex, index) => {
if (activeSectionIndex === -1) {
return 'isDone';
}
if (index < activeSectionIndex) {
return 'isDone';
}
if (index > activeSectionIndex) {
return 'isNotDone';
}
return 'isActive';
}
const StepperHomepage = ({ sections, currentSectionKey }) => {
const activeSectionIndex = sections.findIndex(section => section.key === currentSectionKey);
return (
<GridCustom>
{sections.map((section, index) => (
<StepHomepage
key={section.key}
title={section.title}
content={section.content}
number={index + 1}
type={getType(activeSectionIndex, index)}
hasLine={index !== sections.length - 1}
/>
))}
</GridCustom>
);
};
StepperHomepage.defaultProps = {
currentSectionKey: undefined,
};
StepperHomepage.propTypes = {
sections: PropTypes.arrayOf(
PropTypes.shape({
key: PropTypes.string.isRequired,
title: PropTypes.shape({
id: PropTypes.string,
defaultMessage: PropTypes.string,
}).isRequired,
content: PropTypes.node,
})
).isRequired,
currentSectionKey: PropTypes.string,
};
export default StepperHomepage;

View File

@ -0,0 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import { pxToRem } from '@strapi/helper-plugin';
import { Box } from '@strapi/design-system/Box';
const StepLine = ({ type, ...props }) => {
return (
<Box
width={pxToRem(2)}
height="100%"
background={type === 'isNotDone' ? 'neutral300' : 'primary500'}
hasRadius
{...props}
/>
);
};
StepLine.defaultProps = {
type: 'isNotDone',
};
StepLine.propTypes = {
type: PropTypes.oneOf(['isActive', 'isDone', 'isNotDone']),
};
export default StepLine;

View File

@ -0,0 +1,70 @@
import React from 'react';
import PropTypes from 'prop-types';
import { pxToRem } from '@strapi/helper-plugin';
import { Flex } from '@strapi/design-system/Flex';
import { Typography } from '@strapi/design-system/Typography';
import { Icon } from '@strapi/design-system/Icon';
import Check from '@strapi/icons/Check';
const StepNumber = ({ type, number }) => {
if (type === 'isDone') {
return (
<Flex
background="primary600"
padding={2}
borderRadius="50%"
width={pxToRem(30)}
height={pxToRem(30)}
justifyContent="center"
>
<Icon as={Check} aria-hidden width="16px" color="neutral0" />
</Flex>
);
}
if (type === 'isActive') {
return (
<Flex
background="primary600"
padding={2}
borderRadius="50%"
width={pxToRem(30)}
height={pxToRem(30)}
justifyContent="center"
>
<Typography fontWeight="semiBold" textColor="neutral0">
{number}
</Typography>
</Flex>
);
}
return (
<Flex
borderColor="neutral500"
borderWidth="1px"
borderStyle="solid"
padding={2}
borderRadius="50%"
width={pxToRem(30)}
height={pxToRem(30)}
justifyContent="center"
>
<Typography fontWeight="semiBold" textColor="neutral600">
{number}
</Typography>
</Flex>
);
};
StepNumber.defaultProps = {
number: undefined,
type: 'isNotDone',
};
StepNumber.propTypes = {
number: PropTypes.number,
type: PropTypes.oneOf(['isActive', 'isDone', 'isNotDone']),
};
export default StepNumber;

View File

@ -7,6 +7,26 @@ import { ThemeProvider, lightTheme } from '@strapi/design-system';
import HomePage from '../index';
import { useModels } from '../../../hooks';
jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useGuidedTour: jest.fn(() => ({
guidedTourState: {
apiTokens: {
create: false,
success: false,
},
contentManager: {
create: false,
success: false,
},
contentTypeBuilder: {
create: false,
success: false,
},
},
})),
}));
jest.mock('../../../hooks', () => ({
useModels: jest.fn(),
}));