mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Segment preview (#1194)
* segment preview * fix: loading and font size inconsistencies * update segment accordion styles
This commit is contained in:
		
							parent
							
								
									672a3f0b92
								
							
						
					
					
						commit
						b7de1fba52
					
				| @ -26,9 +26,6 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         '&:before': { | ||||
|             opacity: '0 !important', | ||||
|         }, | ||||
|         '&:first-of-type, &:last-of-type': { | ||||
|             borderRadius: theme.shape.borderRadiusMedium, | ||||
|         }, | ||||
|     }, | ||||
|     accordionEdit: { | ||||
|         backgroundColor: '#F6F6FA', | ||||
|  | ||||
| @ -48,7 +48,7 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         justifyContent: 'space-between', | ||||
|         alignItems: 'center', | ||||
|         fontWeight: theme.fontWeight.bold, | ||||
|         fontSize: theme.fontSizes.subHeader, | ||||
|         fontSize: theme.fontSizes.bodySize, | ||||
|     }, | ||||
|     description: { | ||||
|         color: '#fff', | ||||
|  | ||||
| @ -0,0 +1,45 @@ | ||||
| import { makeStyles } from 'tss-react/mui'; | ||||
| 
 | ||||
| export const useStyles = makeStyles()(theme => ({ | ||||
|     container: { | ||||
|         width: '100%', | ||||
|         padding: theme.spacing(2, 3), | ||||
|         display: 'flex', | ||||
|         alignItems: 'center', | ||||
|         justifyContent: 'flex-start', | ||||
|         fontSize: theme.fontSizes.smallBody, | ||||
|         border: `1px solid ${theme.palette.dividerAlternative}`, | ||||
|         position: 'relative', | ||||
|         borderRadius: '5px', | ||||
|     }, | ||||
|     link: { | ||||
|         textDecoration: 'none', | ||||
|         marginLeft: theme.spacing(1), | ||||
|         '&:hover': { | ||||
|             textDecoration: 'underline', | ||||
|         }, | ||||
|     }, | ||||
|     accordion: { | ||||
|         border: `1px solid ${theme.palette.dividerAlternative}`, | ||||
|         borderRadius: theme.shape.borderRadiusMedium, | ||||
|         backgroundColor: '#fff', | ||||
|         boxShadow: 'none', | ||||
|         margin: 0, | ||||
|     }, | ||||
|     accordionRoot: { | ||||
|         transition: 'all 0.1s ease', | ||||
|     }, | ||||
|     accordionExpanded: { | ||||
|         backgroundColor: theme.palette.neutral.light, | ||||
|     }, | ||||
|     previewButton: { | ||||
|         paddingTop: 0, | ||||
|         paddingBottom: 0, | ||||
|         marginLeft: 'auto', | ||||
|         fontSize: theme.fontSizes.smallBody, | ||||
|     }, | ||||
|     summary: { | ||||
|         fontSize: theme.fontSizes.smallBody, | ||||
|         margin: theme.spacing(0.5, 0), | ||||
|     }, | ||||
| })); | ||||
							
								
								
									
										81
									
								
								frontend/src/component/common/SegmentItem/SegmentItem.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								frontend/src/component/common/SegmentItem/SegmentItem.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| import { useState, VFC } from 'react'; | ||||
| import { Link } from 'react-router-dom'; | ||||
| import { DonutLarge } from '@mui/icons-material'; | ||||
| import { ISegment } from 'interfaces/segment'; | ||||
| import { | ||||
|     Accordion, | ||||
|     AccordionDetails, | ||||
|     AccordionSummary, | ||||
|     Button, | ||||
|     Typography, | ||||
| } from '@mui/material'; | ||||
| import { ConstraintAccordionList } from '../ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList'; | ||||
| import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender'; | ||||
| import { useStyles } from './SegmentItem.styles'; | ||||
| 
 | ||||
| interface ISegmentItemProps { | ||||
|     segment: ISegment; | ||||
|     isExpanded?: boolean; | ||||
| } | ||||
| 
 | ||||
| export const SegmentItem: VFC<ISegmentItemProps> = ({ | ||||
|     segment, | ||||
|     isExpanded, | ||||
| }) => { | ||||
|     const { classes } = useStyles(); | ||||
|     const [isOpen, setIsOpen] = useState(isExpanded || false); | ||||
| 
 | ||||
|     return ( | ||||
|         <Accordion | ||||
|             expanded={isOpen} | ||||
|             className={classes.accordion} | ||||
|             classes={{ | ||||
|                 root: classes.accordionRoot, | ||||
|                 expanded: classes.accordionExpanded, | ||||
|             }} | ||||
|         > | ||||
|             <AccordionSummary | ||||
|                 id={`segment-accordion-${segment.id}`} | ||||
|                 className={classes.summary} | ||||
|             > | ||||
|                 <DonutLarge color="secondary" sx={{ mr: 1 }} /> | ||||
|                 Segment: | ||||
|                 <Link | ||||
|                     to={`/segments/edit/${segment.id}`} | ||||
|                     className={classes.link} | ||||
|                 > | ||||
|                     {segment.name} | ||||
|                 </Link> | ||||
|                 <ConditionallyRender | ||||
|                     condition={!isExpanded} | ||||
|                     show={ | ||||
|                         <Button | ||||
|                             size="small" | ||||
|                             variant="outlined" | ||||
|                             onClick={() => setIsOpen(value => !value)} | ||||
|                             className={classes.previewButton} | ||||
|                         > | ||||
|                             {isOpen ? 'Close preview' : 'Preview'} | ||||
|                         </Button> | ||||
|                     } | ||||
|                 /> | ||||
|             </AccordionSummary> | ||||
|             <AccordionDetails sx={{ pt: 0 }}> | ||||
|                 <ConditionallyRender | ||||
|                     condition={segment!.constraints?.length > 0} | ||||
|                     show={ | ||||
|                         <ConstraintAccordionList | ||||
|                             constraints={segment!.constraints} | ||||
|                             showLabel={false} | ||||
|                         /> | ||||
|                     } | ||||
|                     elseShow={ | ||||
|                         <Typography> | ||||
|                             This segment has no constraints. | ||||
|                         </Typography> | ||||
|                     } | ||||
|                 /> | ||||
|             </AccordionDetails> | ||||
|         </Accordion> | ||||
|     ); | ||||
| }; | ||||
| @ -26,6 +26,7 @@ const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({ | ||||
|     backgroundColor: theme.palette.activityIndicators.primary, | ||||
|     border: `1px solid ${theme.palette.primary.border}`, | ||||
|     borderRadius: theme.shape.borderRadiusLarge, | ||||
|     padding: theme.spacing(0.75, 1.5), | ||||
| })); | ||||
| 
 | ||||
| export const StrategySeparator = ({ text }: IStrategySeparatorProps) => { | ||||
|  | ||||
| @ -16,7 +16,7 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         display: 'grid', | ||||
|         gridTemplateColumns: 'auto 1fr', | ||||
|         gridGap: '.5rem', | ||||
|         fontSize: theme.fontSizes.subHeader, | ||||
|         fontSize: theme.fontSizes.bodySize, | ||||
|     }, | ||||
|     icon: { | ||||
|         color: theme.palette.primary.main, | ||||
|  | ||||
| @ -3,5 +3,7 @@ import { makeStyles } from 'tss-react/mui'; | ||||
| export const useStyles = makeStyles()(theme => ({ | ||||
|     divider: { | ||||
|         border: `1px dashed ${theme.palette.divider}`, | ||||
|         marginTop: theme.spacing(1), | ||||
|         marginBottom: theme.spacing(2), | ||||
|     }, | ||||
| })); | ||||
|  | ||||
| @ -12,10 +12,7 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         gap: '0.5rem', | ||||
|     }, | ||||
|     and: { | ||||
|         color: theme.palette.grey[600], | ||||
|         fontSize: theme.fontSizes.smallerBody, | ||||
|         border: '1px solid', | ||||
|         borderColor: theme.palette.grey[300], | ||||
|         padding: theme.spacing(0.75, 1), | ||||
|         display: 'block', | ||||
|         marginTop: 'auto', | ||||
| @ -23,6 +20,8 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         alignItems: 'center', | ||||
|         borderRadius: theme.shape.borderRadius, | ||||
|         lineHeight: 1, | ||||
|         color: theme.palette.text.primary, | ||||
|         backgroundColor: theme.palette.secondaryContainer, | ||||
|     }, | ||||
|     selectedSegmentsLabel: { | ||||
|         color: theme.palette.text.secondary, | ||||
|  | ||||
| @ -3,7 +3,7 @@ import { ISegment } from 'interfaces/segment'; | ||||
| import { useStyles } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentList.styles'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| import { FeatureStrategySegmentChip } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentChip'; | ||||
| import { ConstraintAccordionList } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList'; | ||||
| import { SegmentItem } from 'component/common/SegmentItem/SegmentItem'; | ||||
| 
 | ||||
| interface IFeatureStrategySegmentListProps { | ||||
|     segments: ISegment[]; | ||||
| @ -48,16 +48,10 @@ export const FeatureStrategySegmentList = ({ | ||||
|                     </Fragment> | ||||
|                 ))} | ||||
|             </div> | ||||
|             <ConditionallyRender | ||||
|                 condition={Boolean(preview && preview.constraints.length === 0)} | ||||
|                 show={() => <p>This segment has no constraints.</p>} | ||||
|             /> | ||||
|             <ConditionallyRender | ||||
|                 condition={Boolean(preview)} | ||||
|                 show={() => ( | ||||
|                     <ConstraintAccordionList | ||||
|                         constraints={preview!.constraints} | ||||
|                     /> | ||||
|                     <SegmentItem segment={preview as ISegment} isExpanded /> | ||||
|                 )} | ||||
|             /> | ||||
|         </> | ||||
|  | ||||
| @ -19,7 +19,7 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         }, | ||||
|     }, | ||||
|     header: { | ||||
|         fontSize: theme.fontSizes.subHeader, | ||||
|         fontSize: theme.fontSizes.bodySize, | ||||
|         fontWeight: 'normal', | ||||
|         margin: 0, | ||||
|         marginBottom: '0.5rem', | ||||
|  | ||||
| @ -31,7 +31,7 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|     percentage: { | ||||
|         color: theme.palette.primary.main, | ||||
|         textAlign: 'right', | ||||
|         fontSize: theme.fontSizes.subHeader, | ||||
|         fontSize: theme.fontSizes.bodySize, | ||||
|     }, | ||||
|     percentageCircle: { | ||||
|         margin: '0 1rem', | ||||
|  | ||||
| @ -19,7 +19,7 @@ const SeparatorContainer = styled('div')(({ theme }) => ({ | ||||
| })); | ||||
| 
 | ||||
| const SeparatorContent = styled('span')(({ theme }) => ({ | ||||
|     fontSize: theme.fontSizes.subHeader, | ||||
|     fontSize: theme.fontSizes.bodySize, | ||||
|     textAlign: 'center', | ||||
|     padding: '0 1rem', | ||||
|     background: theme.palette.secondaryContainer, | ||||
|  | ||||
| @ -28,7 +28,7 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         alignItems: 'center', | ||||
|     }, | ||||
|     header: { | ||||
|         fontSize: theme.fontSizes.subHeader, | ||||
|         fontSize: theme.fontSizes.bodySize, | ||||
|         fontWeight: 'normal', | ||||
|         margin: 0, | ||||
|     }, | ||||
|  | ||||
| @ -1,22 +0,0 @@ | ||||
| import { makeStyles } from 'tss-react/mui'; | ||||
| 
 | ||||
| export const useStyles = makeStyles()(theme => ({ | ||||
|     container: { | ||||
|         width: '100%', | ||||
|         padding: theme.spacing(2, 3), | ||||
|         display: 'flex', | ||||
|         alignItems: 'center', | ||||
|         justifyContent: 'flex-start', | ||||
|         fontSize: theme.fontSizes.smallBody, | ||||
|         border: `1px solid ${theme.palette.dividerAlternative}`, | ||||
|         position: 'relative', | ||||
|         borderRadius: '5px', | ||||
|     }, | ||||
|     link: { | ||||
|         textDecoration: 'none', | ||||
|         marginLeft: theme.spacing(1), | ||||
|         '&:hover': { | ||||
|             textDecoration: 'underline', | ||||
|         }, | ||||
|     }, | ||||
| })); | ||||
| @ -1,10 +1,8 @@ | ||||
| import { Fragment } from 'react'; | ||||
| import { Link } from 'react-router-dom'; | ||||
| import { DonutLarge } from '@mui/icons-material'; | ||||
| import { useStyles } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment.styles'; | ||||
| import { useSegments } from 'hooks/api/getters/useSegments/useSegments'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; | ||||
| import { SegmentItem } from '../../../../common/SegmentItem/SegmentItem'; | ||||
| 
 | ||||
| interface IFeatureOverviewSegmentProps { | ||||
|     strategyId: string; | ||||
| @ -13,7 +11,6 @@ interface IFeatureOverviewSegmentProps { | ||||
| export const FeatureOverviewSegment = ({ | ||||
|     strategyId, | ||||
| }: IFeatureOverviewSegmentProps) => { | ||||
|     const { classes: styles } = useStyles(); | ||||
|     const { segments } = useSegments(strategyId); | ||||
| 
 | ||||
|     if (!segments || segments.length === 0) { | ||||
| @ -28,21 +25,9 @@ export const FeatureOverviewSegment = ({ | ||||
|                         condition={index > 0} | ||||
|                         show={<StrategySeparator text="AND" />} | ||||
|                     /> | ||||
|                     <div className={styles.container}> | ||||
|                         <DonutLarge color="secondary" sx={{ mr: 1 }} /> Segment:{' '} | ||||
|                         <Link | ||||
|                             to={segmentPath(segment.id)} | ||||
|                             className={styles.link} | ||||
|                         > | ||||
|                             {segment.name} | ||||
|                         </Link> | ||||
|                     </div> | ||||
|                     <SegmentItem segment={segment} /> | ||||
|                 </Fragment> | ||||
|             ))} | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
| 
 | ||||
| const segmentPath = (segmentId: number): string => { | ||||
|     return `/segments/edit/${segmentId}`; | ||||
| }; | ||||
|  | ||||
| @ -76,21 +76,6 @@ export const FeatureView = () => { | ||||
| 
 | ||||
|     const activeTab = tabData.find(tab => tab.path === pathname) ?? tabData[0]; | ||||
| 
 | ||||
|     const renderTabs = () => { | ||||
|         return tabData.map((tab, index) => { | ||||
|             return ( | ||||
|                 <Tab | ||||
|                     data-loading | ||||
|                     key={tab.title} | ||||
|                     label={tab.title} | ||||
|                     value={tab.path} | ||||
|                     onClick={() => navigate(tab.path)} | ||||
|                     className={styles.tabButton} | ||||
|                 /> | ||||
|             ); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     if (status === 404) { | ||||
|         return <FeatureNotFound />; | ||||
|     } | ||||
| @ -168,7 +153,15 @@ export const FeatureView = () => { | ||||
|                                 indicatorColor="primary" | ||||
|                                 textColor="primary" | ||||
|                             > | ||||
|                                 {renderTabs()} | ||||
|                                 {tabData.map(tab => ( | ||||
|                                     <Tab | ||||
|                                         key={tab.title} | ||||
|                                         label={tab.title} | ||||
|                                         value={tab.path} | ||||
|                                         onClick={() => navigate(tab.path)} | ||||
|                                         className={styles.tabButton} | ||||
|                                     /> | ||||
|                                 ))} | ||||
|                             </Tabs> | ||||
|                         </div> | ||||
|                     </div> | ||||
|  | ||||
| @ -80,21 +80,6 @@ const Project = () => { | ||||
|         /* eslint-disable-next-line */ | ||||
|     }, []); | ||||
| 
 | ||||
|     const renderTabs = () => { | ||||
|         return tabs.map(tab => { | ||||
|             return ( | ||||
|                 <Tab | ||||
|                     data-loading | ||||
|                     key={tab.title} | ||||
|                     label={tab.title} | ||||
|                     value={tab.path} | ||||
|                     onClick={() => navigate(tab.path)} | ||||
|                     className={styles.tabButton} | ||||
|                 /> | ||||
|             ); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     return ( | ||||
|         <div ref={ref}> | ||||
|             <div className={styles.header}> | ||||
| @ -135,7 +120,15 @@ const Project = () => { | ||||
|                         indicatorColor="primary" | ||||
|                         textColor="primary" | ||||
|                     > | ||||
|                         {renderTabs()} | ||||
|                         {tabs.map(tab => ( | ||||
|                             <Tab | ||||
|                                 key={tab.title} | ||||
|                                 label={tab.title} | ||||
|                                 value={tab.path} | ||||
|                                 onClick={() => navigate(tab.path)} | ||||
|                                 className={styles.tabButton} | ||||
|                             /> | ||||
|                         ))} | ||||
|                     </Tabs> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
| @ -36,7 +36,6 @@ export default createTheme({ | ||||
|     }, | ||||
|     fontSizes: { | ||||
|         mainHeader: '1.25rem', | ||||
|         subHeader: '1.1rem', | ||||
|         bodySize: '1rem', | ||||
|         smallBody: `${14 / 16}rem`, | ||||
|         smallerBody: `${12 / 16}rem`, | ||||
| @ -260,12 +259,24 @@ export default createTheme({ | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|         MuiAccordion: { | ||||
|             styleOverrides: { | ||||
|                 root: ({ theme }) => ({ | ||||
|                     '&:first-of-type, &:last-of-type': { | ||||
|                         borderRadius: theme.shape.borderRadiusMedium, | ||||
|                     }, | ||||
|                 }), | ||||
|             }, | ||||
|         }, | ||||
|         MuiAccordionSummary: { | ||||
|             styleOverrides: { | ||||
|                 root: { | ||||
|                     '& > .MuiAccordionSummary-content.Mui-expanded': { | ||||
|                         margin: '12px 0', | ||||
|                     }, | ||||
|                     '&.Mui-expanded': { | ||||
|                         minHeight: '0', | ||||
|                     }, | ||||
|                 }, | ||||
|             }, | ||||
|         }, | ||||
|  | ||||
| @ -5,7 +5,6 @@ declare module '@mui/material/styles' { | ||||
|          */ | ||||
|         fontSizes: { | ||||
|             mainHeader: string; | ||||
|             subHeader: string; | ||||
|             bodySize: string; | ||||
|             smallBody: string; | ||||
|             smallerBody: string; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user