1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-31 13:47:02 +02:00

refactor: batch of changes for styled components (#2791)

This commit is contained in:
Mateusz Kwasniewski 2023-01-03 09:20:26 +01:00 committed by GitHub
parent 5fe16207db
commit 231b26995c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 411 additions and 494 deletions

View File

@ -1,9 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
label: {
display: 'inline-flex',
alignItems: 'center',
cursor: 'pointer',
},
}));

View File

@ -8,12 +8,12 @@ import StringTruncator from 'component/common/StringTruncator/StringTruncator';
import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions'; import { UPDATE_FEATURE_ENVIRONMENT } from 'component/providers/AccessProvider/permissions';
import React from 'react'; import React from 'react';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import { useStyles } from './FeatureOverviewEnvSwitch.styles';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle'; import { useChangeRequestToggle } from 'hooks/useChangeRequestToggle';
import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog'; import { ChangeRequestDialogue } from 'component/changeRequest/ChangeRequestConfirmDialog/ChangeRequestConfirmDialog';
import { UpdateEnabledMessage } from '../../../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage'; import { UpdateEnabledMessage } from '../../../../../changeRequest/ChangeRequestConfirmDialog/ChangeRequestMessages/UpdateEnabledMessage';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { styled } from '@mui/material';
interface IFeatureOverviewEnvSwitchProps { interface IFeatureOverviewEnvSwitchProps {
env: IFeatureEnvironment; env: IFeatureEnvironment;
@ -22,6 +22,12 @@ interface IFeatureOverviewEnvSwitchProps {
showInfoBox: () => void; showInfoBox: () => void;
} }
const StyledLabel = styled('label')({
display: 'inline-flex',
alignItems: 'center',
cursor: 'pointer',
});
const FeatureOverviewEnvSwitch = ({ const FeatureOverviewEnvSwitch = ({
env, env,
callback, callback,
@ -34,7 +40,6 @@ const FeatureOverviewEnvSwitch = ({
useFeatureApi(); useFeatureApi();
const { refetchFeature } = useFeature(projectId, featureId); const { refetchFeature } = useFeature(projectId, featureId);
const { setToastData, setToastApiError } = useToast(); const { setToastData, setToastApiError } = useToast();
const { classes: styles } = useStyles();
const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId); const { isChangeRequestConfigured } = useChangeRequestsEnabled(projectId);
const { const {
onChangeRequestToggle, onChangeRequestToggle,
@ -110,7 +115,7 @@ const FeatureOverviewEnvSwitch = ({
return ( return (
<div> <div>
<label className={styles.label}> <StyledLabel>
<PermissionSwitch <PermissionSwitch
permission={UPDATE_FEATURE_ENVIRONMENT} permission={UPDATE_FEATURE_ENVIRONMENT}
projectId={projectId} projectId={projectId}
@ -119,7 +124,7 @@ const FeatureOverviewEnvSwitch = ({
environmentId={env.name} environmentId={env.name}
/> />
{content} {content}
</label> </StyledLabel>
<ChangeRequestDialogue <ChangeRequestDialogue
isOpen={changeRequestDialogDetails.isOpen} isOpen={changeRequestDialogDetails.isOpen}
onClose={onChangeRequestToggleClose} onClose={onChangeRequestToggleClose}

View File

@ -1,14 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
accordionBodyInnerContainer: {
[theme.breakpoints.down(400)]: {
padding: '0.5rem',
},
},
accordionBody: {
width: '100%',
position: 'relative',
paddingBottom: '1rem',
},
}));

View File

@ -1,5 +1,5 @@
import { DragEventHandler, RefObject, useEffect, useState } from 'react'; import { DragEventHandler, RefObject, useEffect, useState } from 'react';
import { Alert } from '@mui/material'; import { Alert, styled } from '@mui/material';
import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi'; import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import useToast from 'hooks/useToast'; import useToast from 'hooks/useToast';
@ -8,7 +8,6 @@ import { StrategyDraggableItem } from './StrategyDraggableItem/StrategyDraggable
import { IFeatureEnvironment } from 'interfaces/featureToggle'; import { IFeatureEnvironment } from 'interfaces/featureToggle';
import { FeatureStrategyEmpty } from 'component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty'; import { FeatureStrategyEmpty } from 'component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { useStyles } from './EnvironmentAccordionBody.styles';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
interface IEnvironmentAccordionBodyProps { interface IEnvironmentAccordionBodyProps {
@ -17,6 +16,18 @@ interface IEnvironmentAccordionBodyProps {
otherEnvironments?: IFeatureEnvironment['name'][]; otherEnvironments?: IFeatureEnvironment['name'][];
} }
const StyledAccordionBody = styled('div')(({ theme }) => ({
width: '100%',
position: 'relative',
paddingBottom: theme.spacing(2),
}));
const StyledAccordionBodyInnerContainer = styled('div')(({ theme }) => ({
[theme.breakpoints.down(400)]: {
padding: theme.spacing(1),
},
}));
const EnvironmentAccordionBody = ({ const EnvironmentAccordionBody = ({
featureEnvironment, featureEnvironment,
isDisabled, isDisabled,
@ -35,7 +46,6 @@ const EnvironmentAccordionBody = ({
index: number; index: number;
height: number; height: number;
} | null>(null); } | null>(null);
const { classes: styles } = useStyles();
useEffect(() => { useEffect(() => {
// Use state to enable drag and drop, but switch to API output when it arrives // Use state to enable drag and drop, but switch to API output when it arrives
setStrategies(featureEnvironment?.strategies || []); setStrategies(featureEnvironment?.strategies || []);
@ -128,8 +138,8 @@ const EnvironmentAccordionBody = ({
}; };
return ( return (
<div className={styles.accordionBody}> <StyledAccordionBody>
<div className={styles.accordionBodyInnerContainer}> <StyledAccordionBodyInnerContainer>
<ConditionallyRender <ConditionallyRender
condition={strategies.length > 0 && isDisabled} condition={strategies.length > 0 && isDisabled}
show={() => ( show={() => (
@ -166,8 +176,8 @@ const EnvironmentAccordionBody = ({
/> />
} }
/> />
</div> </StyledAccordionBodyInnerContainer>
</div> </StyledAccordionBody>
); );
}; };

View File

@ -1,20 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
width: '100%',
padding: theme.spacing(2, 3),
borderRadius: theme.shape.borderRadiusMedium,
border: `1px solid ${theme.palette.divider}`,
},
chip: {
margin: '0.25rem',
},
paragraph: {
display: 'inline',
margin: '0.25rem 0',
maxWidth: '95%',
textAlign: 'center',
wordBreak: 'break-word',
},
}));

View File

@ -1,6 +1,5 @@
import { Chip } from '@mui/material'; import { Chip, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useStyles } from './ConstraintItem.styles';
import StringTruncator from 'component/common/StringTruncator/StringTruncator'; import StringTruncator from 'component/common/StringTruncator/StringTruncator';
interface IConstraintItemProps { interface IConstraintItemProps {
@ -8,22 +7,40 @@ interface IConstraintItemProps {
text: string; text: string;
} }
const StyledContainer = styled('div')(({ theme }) => ({
width: '100%',
padding: theme.spacing(2, 3),
borderRadius: theme.shape.borderRadiusMedium,
border: `1px solid ${theme.palette.divider}`,
}));
const StyledParagraph = styled('p')(({ theme }) => ({
display: 'inline',
margin: theme.spacing(0.5, 0),
maxWidth: '95%',
textAlign: 'center',
wordBreak: 'break-word',
}));
const StyledChip = styled(Chip)(({ theme }) => ({
margin: theme.spacing(0.5),
}));
export const ConstraintItem = ({ value, text }: IConstraintItemProps) => { export const ConstraintItem = ({ value, text }: IConstraintItemProps) => {
const { classes: styles } = useStyles();
return ( return (
<div className={styles.container}> <StyledContainer>
<ConditionallyRender <ConditionallyRender
condition={value.length === 0} condition={value.length === 0}
show={<p>No {text}s added yet.</p>} show={<p>No {text}s added yet.</p>}
elseShow={ elseShow={
<div> <div>
<p className={styles.paragraph}> <StyledParagraph>
{value.length}{' '} {value.length}{' '}
{value.length > 1 ? `${text}s` : text} will get {value.length > 1 ? `${text}s` : text} will get
access. access.
</p> </StyledParagraph>
{value.map((v: string) => ( {value.map((v: string) => (
<Chip <StyledChip
key={v} key={v}
label={ label={
<StringTruncator <StringTruncator
@ -32,12 +49,11 @@ export const ConstraintItem = ({ value, text }: IConstraintItemProps) => {
maxLength={50} maxLength={50}
/> />
} }
className={styles.chip}
/> />
))} ))}
</div> </div>
} }
/> />
</div> </StyledContainer>
); );
}; };

View File

@ -1,12 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
valueContainer: {
padding: theme.spacing(2, 3),
border: `1px solid ${theme.palette.dividerAlternative}`,
borderRadius: theme.shape.borderRadiusMedium,
},
valueSeparator: {
color: theme.palette.grey[700],
},
}));

View File

@ -1,6 +1,6 @@
import { Fragment, useMemo, VFC } from 'react'; import { Fragment, useMemo, VFC } from 'react';
import { Box, Chip } from '@mui/material'; import { Box, Chip, styled } from '@mui/material';
import { IFeatureStrategy, IFeatureStrategyPayload } from 'interfaces/strategy'; import { IFeatureStrategyPayload } from 'interfaces/strategy';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle'; import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator'; import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
@ -10,7 +10,6 @@ import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { FeatureOverviewSegment } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment'; import { FeatureOverviewSegment } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment';
import { ConstraintAccordionList } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList'; import { ConstraintAccordionList } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
import { useStyles } from './StrategyExecution.styles';
import { import {
parseParameterNumber, parseParameterNumber,
parseParameterString, parseParameterString,
@ -28,11 +27,20 @@ const NoItems: VFC = () => (
</Box> </Box>
); );
const StyledValueContainer = styled(Box)(({ theme }) => ({
padding: theme.spacing(2, 3),
border: `1px solid ${theme.palette.dividerAlternative}`,
borderRadius: theme.shape.borderRadiusMedium,
}));
const StyledValueSeparator = styled('span')(({ theme }) => ({
color: theme.palette.neutral.main,
}));
export const StrategyExecution: VFC<IStrategyExecutionProps> = ({ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
strategy, strategy,
}) => { }) => {
const { parameters, constraints = [] } = strategy; const { parameters, constraints = [] } = strategy;
const { classes: styles } = useStyles();
const { strategies } = useStrategies(); const { strategies } = useStrategies();
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const { segments } = useSegments(); const { segments } = useSegments();
@ -54,8 +62,7 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
const percentage = parseParameterNumber(parameters[key]); const percentage = parseParameterNumber(parameters[key]);
return ( return (
<Box <StyledValueContainer
className={styles.valueContainer}
sx={{ display: 'flex', alignItems: 'center' }} sx={{ display: 'flex', alignItems: 'center' }}
> >
<Box sx={{ mr: 2 }}> <Box sx={{ mr: 2 }}>
@ -77,7 +84,7 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
: ''}{' '} : ''}{' '}
is included. is included.
</div> </div>
</Box> </StyledValueContainer>
); );
case 'userIds': case 'userIds':
case 'UserIds': case 'UserIds':
@ -101,12 +108,12 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
return null; return null;
} }
}); });
}, [parameters, definition, constraints, styles]); }, [parameters, definition, constraints]);
const customStrategyList = useMemo(() => { const customStrategyList = useMemo(() => {
if (!parameters || !definition?.editable) return null; if (!parameters || !definition?.editable) return null;
const isSetTo = ( const isSetTo = (
<span className={styles.valueSeparator}>{' is set to '}</span> <StyledValueSeparator>{' is set to '}</StyledValueSeparator>
); );
return definition?.parameters.map(param => { return definition?.parameters.map(param => {
@ -123,9 +130,9 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
const values = parseParameterStrings(parameters[name]); const values = parseParameterStrings(parameters[name]);
return values.length > 0 ? ( return values.length > 0 ? (
<div className={styles.valueContainer}> <StyledValueContainer>
{nameItem}{' '} {nameItem}{' '}
<span className={styles.valueSeparator}> <StyledValueSeparator>
has {values.length}{' '} has {values.length}{' '}
{values.length > 1 ? `items` : 'item'}:{' '} {values.length > 1 ? `items` : 'item'}:{' '}
{values.map((item: string) => ( {values.map((item: string) => (
@ -141,15 +148,14 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
sx={{ mr: 0.5 }} sx={{ mr: 0.5 }}
/> />
))} ))}
</span> </StyledValueSeparator>
</div> </StyledValueContainer>
) : null; ) : null;
case 'percentage': case 'percentage':
const percentage = parseParameterNumber(parameters[name]); const percentage = parseParameterNumber(parameters[name]);
return parameters[name] !== '' ? ( return parameters[name] !== '' ? (
<Box <StyledValueContainer
className={styles.valueContainer}
sx={{ display: 'flex', alignItems: 'center' }} sx={{ display: 'flex', alignItems: 'center' }}
> >
<Box sx={{ mr: 2 }}> <Box sx={{ mr: 2 }}>
@ -168,13 +174,13 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
label={`${percentage}%`} label={`${percentage}%`}
/> />
</div> </div>
</Box> </StyledValueContainer>
) : null; ) : null;
case 'boolean': case 'boolean':
return parameters[name] === 'true' || return parameters[name] === 'true' ||
parameters[name] === 'false' ? ( parameters[name] === 'false' ? (
<div className={styles.valueContainer}> <StyledValueContainer>
<StringTruncator <StringTruncator
maxLength={15} maxLength={15}
maxWidth="150" maxWidth="150"
@ -191,20 +197,20 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
size="small" size="small"
label={parameters[name]} label={parameters[name]}
/> />
</div> </StyledValueContainer>
) : null; ) : null;
case 'string': case 'string':
const value = parseParameterString(parameters[name]); const value = parseParameterString(parameters[name]);
return typeof parameters[name] !== 'undefined' ? ( return typeof parameters[name] !== 'undefined' ? (
<div className={styles.valueContainer}> <StyledValueContainer>
{nameItem} {nameItem}
<ConditionallyRender <ConditionallyRender
condition={value === ''} condition={value === ''}
show={ show={
<span className={styles.valueSeparator}> <StyledValueSeparator>
{' is an empty string'} {' is an empty string'}
</span> </StyledValueSeparator>
} }
elseShow={ elseShow={
<> <>
@ -217,13 +223,13 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
</> </>
} }
/> />
</div> </StyledValueContainer>
) : null; ) : null;
case 'number': case 'number':
const number = parseParameterNumber(parameters[name]); const number = parseParameterNumber(parameters[name]);
return parameters[name] !== '' && number !== undefined ? ( return parameters[name] !== '' && number !== undefined ? (
<div className={styles.valueContainer}> <StyledValueContainer>
{nameItem} {nameItem}
{isSetTo} {isSetTo}
<StringTruncator <StringTruncator
@ -231,7 +237,7 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
text={String(number)} text={String(number)}
maxLength={50} maxLength={50}
/> />
</div> </StyledValueContainer>
) : null; ) : null;
case 'default': case 'default':
return null; return null;
@ -239,7 +245,7 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
return null; return null;
}); });
}, [parameters, definition, styles]); }, [parameters, definition]);
if (!parameters) { if (!parameters) {
return <NoItems />; return <NoItems />;
@ -259,7 +265,7 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
), ),
strategy.name === 'default' && ( strategy.name === 'default' && (
<> <>
<Box sx={{ width: '100%' }} className={styles.valueContainer}> <StyledValueContainer sx={{ width: '100%' }}>
The standard strategy is{' '} The standard strategy is{' '}
<Chip <Chip
variant="outlined" variant="outlined"
@ -268,7 +274,7 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
label="ON" label="ON"
/>{' '} />{' '}
for all users. for all users.
</Box> </StyledValueContainer>
</> </>
), ),
...(parametersList ?? []), ...(parametersList ?? []),

View File

@ -1,98 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
featureOverviewEnvironment: {
borderRadius: theme.shape.borderRadiusLarge,
marginBottom: theme.spacing(2),
backgroundColor: theme.palette.background.paper,
},
accordion: {
boxShadow: 'none',
background: 'none',
},
accordionHeader: {
boxShadow: 'none',
padding: '1rem 2rem',
[theme.breakpoints.down(400)]: {
padding: '0.5rem 1rem',
},
},
accordionBodyInnerContainer: {
[theme.breakpoints.down(400)]: {
padding: '0.5rem',
},
},
accordionDetails: {
padding: theme.spacing(3),
background: theme.palette.secondaryContainer,
borderBottomLeftRadius: theme.shape.borderRadiusLarge,
borderBottomRightRadius: theme.shape.borderRadiusLarge,
borderBottom: `4px solid ${theme.palette.primary.light}`,
[theme.breakpoints.down('md')]: {
padding: theme.spacing(2, 1),
},
},
accordionDetailsDisabled: {
borderBottom: `4px solid ${theme.palette.neutral.border}`,
},
accordionBody: {
width: '100%',
position: 'relative',
paddingBottom: theme.spacing(2),
},
header: {
display: 'flex',
justifyContent: 'center',
flexDirection: 'column',
},
headerTitle: {
display: 'flex',
alignItems: 'center',
[theme.breakpoints.down(560)]: {
flexDirection: 'column',
textAlign: 'center',
},
},
headerIcon: {
[theme.breakpoints.down(560)]: {
marginBottom: '0.5rem',
},
},
iconContainer: {
backgroundColor: theme.palette.primary.light,
borderRadius: '50%',
width: '28px',
height: '28px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginRight: '0.5rem',
},
icon: {
fill: '#fff',
width: '17px',
height: '17px',
},
linkContainer: {
display: 'flex',
justifyContent: 'flex-end',
marginBottom: '1rem',
},
truncator: {
fontSize: theme.fontSizes.bodySize,
fontWeight: theme.typography.fontWeightMedium,
[theme.breakpoints.down(560)]: {
textAlign: 'center',
},
},
container: {
display: 'flex',
alignItems: 'center',
marginLeft: '1.8rem',
[theme.breakpoints.down(560)]: {
flexDirection: 'column',
marginLeft: '0',
},
},
}));

View File

@ -4,9 +4,8 @@ import {
AccordionSummary, AccordionSummary,
Box, Box,
Chip, Chip,
useTheme, styled,
} from '@mui/material'; } from '@mui/material';
import classNames from 'classnames';
import { ExpandMore } from '@mui/icons-material'; import { ExpandMore } from '@mui/icons-material';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import useFeatureMetrics from 'hooks/api/getters/useFeatureMetrics/useFeatureMetrics'; import useFeatureMetrics from 'hooks/api/getters/useFeatureMetrics/useFeatureMetrics';
@ -15,7 +14,6 @@ import { getFeatureMetrics } from 'utils/getFeatureMetrics';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon'; import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon';
import StringTruncator from 'component/common/StringTruncator/StringTruncator'; import StringTruncator from 'component/common/StringTruncator/StringTruncator';
import { useStyles } from './FeatureOverviewEnvironment.styles';
import EnvironmentAccordionBody from './EnvironmentAccordionBody/EnvironmentAccordionBody'; import EnvironmentAccordionBody from './EnvironmentAccordionBody/EnvironmentAccordionBody';
import { EnvironmentFooter } from './EnvironmentFooter/EnvironmentFooter'; import { EnvironmentFooter } from './EnvironmentFooter/EnvironmentFooter';
import FeatureOverviewEnvironmentMetrics from './FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics'; import FeatureOverviewEnvironmentMetrics from './FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics';
@ -23,17 +21,104 @@ import { FeatureStrategyMenu } from 'component/feature/FeatureStrategy/FeatureSt
import { FEATURE_ENVIRONMENT_ACCORDION } from 'utils/testIds'; import { FEATURE_ENVIRONMENT_ACCORDION } from 'utils/testIds';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { FeatureStrategyIcons } from 'component/feature/FeatureStrategy/FeatureStrategyIcons/FeatureStrategyIcons'; import { FeatureStrategyIcons } from 'component/feature/FeatureStrategy/FeatureStrategyIcons/FeatureStrategyIcons';
// import { Badge } from 'component/common/Badge/Badge';
interface IFeatureOverviewEnvironmentProps { interface IFeatureOverviewEnvironmentProps {
env: IFeatureEnvironment; env: IFeatureEnvironment;
} }
const StyledFeatureOverviewEnvironment = styled('div', {
shouldForwardProp: prop => prop !== 'enabled',
})<{ enabled: boolean }>(({ theme, enabled }) => ({
borderRadius: theme.shape.borderRadiusLarge,
marginBottom: theme.spacing(2),
backgroundColor: theme.palette.background.paper,
background: enabled
? theme.palette.background.paper
: theme.palette.neutral.light,
}));
const StyledAccordion = styled(Accordion)({
boxShadow: 'none',
background: 'none',
});
const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(2, 4),
[theme.breakpoints.down(400)]: {
padding: theme.spacing(1, 2),
},
}));
const StyledAccordionDetails = styled(AccordionDetails, {
shouldForwardProp: prop => prop !== 'enabled',
})<{ enabled: boolean }>(({ theme, enabled }) => ({
padding: theme.spacing(3),
background: theme.palette.secondaryContainer,
borderBottomLeftRadius: theme.shape.borderRadiusLarge,
borderBottomRightRadius: theme.shape.borderRadiusLarge,
borderBottom: `4px solid ${
enabled ? theme.palette.primary.light : theme.palette.neutral.border
}`,
[theme.breakpoints.down('md')]: {
padding: theme.spacing(2, 1),
},
}));
const StyledEnvironmentAccordionBody = styled(EnvironmentAccordionBody)(
({ theme }) => ({
width: '100%',
position: 'relative',
paddingBottom: theme.spacing(2),
})
);
const StyledHeader = styled('div', {
shouldForwardProp: prop => prop !== 'enabled',
})<{ enabled: boolean }>(({ theme, enabled }) => ({
display: 'flex',
justifyContent: 'center',
flexDirection: 'column',
color: enabled ? theme.palette.text.primary : theme.palette.text.secondary,
}));
const StyledHeaderTitle = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
[theme.breakpoints.down(560)]: {
flexDirection: 'column',
textAlign: 'center',
},
}));
const StyledEnvironmentIcon = styled(EnvironmentIcon)(({ theme }) => ({
[theme.breakpoints.down(560)]: {
marginBottom: '0.5rem',
},
}));
const StyledStringTruncator = styled(StringTruncator)(({ theme }) => ({
fontSize: theme.fontSizes.bodySize,
fontWeight: theme.typography.fontWeightMedium,
[theme.breakpoints.down(560)]: {
textAlign: 'center',
},
}));
const StyledContainer = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
marginLeft: '1.8rem',
[theme.breakpoints.down(560)]: {
flexDirection: 'column',
marginLeft: '0',
},
}));
const FeatureOverviewEnvironment = ({ const FeatureOverviewEnvironment = ({
env, env,
}: IFeatureOverviewEnvironmentProps) => { }: IFeatureOverviewEnvironmentProps) => {
const { classes: styles } = useStyles();
const theme = useTheme();
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId'); const featureId = useRequiredPathParam('featureId');
const { metrics } = useFeatureMetrics(projectId, featureId); const { metrics } = useFeatureMetrics(projectId, featureId);
@ -48,40 +133,19 @@ const FeatureOverviewEnvironment = ({
); );
return ( return (
<div <StyledFeatureOverviewEnvironment enabled={env.enabled}>
className={styles.featureOverviewEnvironment} <StyledAccordion
style={{
background: !env.enabled
? theme.palette.neutral.light
: theme.palette.background.paper,
}}
>
<Accordion
className={styles.accordion}
data-testid={`${FEATURE_ENVIRONMENT_ACCORDION}_${env.name}`} data-testid={`${FEATURE_ENVIRONMENT_ACCORDION}_${env.name}`}
> >
<AccordionSummary <StyledAccordionSummary
className={styles.accordionHeader}
expandIcon={<ExpandMore titleAccess="Toggle" />} expandIcon={<ExpandMore titleAccess="Toggle" />}
> >
<div <StyledHeader data-loading enabled={env.enabled}>
className={styles.header} <StyledHeaderTitle>
data-loading <StyledEnvironmentIcon enabled={env.enabled} />
style={{
color: !env.enabled
? theme.palette.text.secondary
: theme.palette.text.primary,
}}
>
<div className={styles.headerTitle}>
<EnvironmentIcon
enabled={env.enabled}
className={styles.headerIcon}
/>
<div> <div>
<StringTruncator <StyledStringTruncator
text={env.name} text={env.name}
className={styles.truncator}
maxWidth="100" maxWidth="100"
maxLength={15} maxLength={15}
/> />
@ -97,8 +161,8 @@ const FeatureOverviewEnvironment = ({
/> />
} }
/> />
</div> </StyledHeaderTitle>
<div className={styles.container}> <StyledContainer>
<FeatureStrategyMenu <FeatureStrategyMenu
label="Add strategy" label="Add strategy"
projectId={projectId} projectId={projectId}
@ -109,21 +173,17 @@ const FeatureOverviewEnvironment = ({
<FeatureStrategyIcons <FeatureStrategyIcons
strategies={featureEnvironment?.strategies} strategies={featureEnvironment?.strategies}
/> />
</div> </StyledContainer>
</div> </StyledHeader>
<FeatureOverviewEnvironmentMetrics <FeatureOverviewEnvironmentMetrics
environmentMetric={environmentMetric} environmentMetric={environmentMetric}
disabled={!env.enabled} disabled={!env.enabled}
/> />
</AccordionSummary> </StyledAccordionSummary>
<AccordionDetails <StyledAccordionDetails enabled={env.enabled}>
className={classNames(styles.accordionDetails, { <StyledEnvironmentAccordionBody
[styles.accordionDetailsDisabled]: !env.enabled,
})}
>
<EnvironmentAccordionBody
featureEnvironment={featureEnvironment} featureEnvironment={featureEnvironment}
isDisabled={!env.enabled} isDisabled={!env.enabled}
otherEnvironments={feature?.environments otherEnvironments={feature?.environments
@ -156,9 +216,9 @@ const FeatureOverviewEnvironment = ({
</> </>
} }
/> />
</AccordionDetails> </StyledAccordionDetails>
</Accordion> </StyledAccordion>
</div> </StyledFeatureOverviewEnvironment>
); );
}; };

View File

@ -1,42 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
marginLeft: 'auto',
display: 'flex',
alignItems: 'center',
},
info: {
marginRight: '0.5rem',
display: 'flex',
flexDirection: 'column',
},
icon: {
fill: theme.palette.grey[300],
height: '75px',
width: '75px',
[theme.breakpoints.down(500)]: {
display: 'none',
},
},
infoParagraph: {
maxWidth: '270px',
marginTop: '0.25rem',
fontSize: theme.fontSizes.smallBody,
textAlign: 'right',
[theme.breakpoints.down(700)]: {
display: 'none',
},
},
percentage: {
color: theme.palette.primary.main,
textAlign: 'right',
fontSize: theme.fontSizes.bodySize,
},
percentageCircle: {
margin: '0 1rem',
[theme.breakpoints.down(500)]: {
display: 'none',
},
},
}));

View File

@ -3,19 +3,62 @@ import { useTheme } from '@mui/system';
import { IFeatureEnvironmentMetrics } from 'interfaces/featureToggle'; import { IFeatureEnvironmentMetrics } from 'interfaces/featureToggle';
import { calculatePercentage } from 'utils/calculatePercentage'; import { calculatePercentage } from 'utils/calculatePercentage';
import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle'; import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle';
import { useStyles } from './FeatureOverviewEnvironmentMetrics.styles';
import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber'; import { PrettifyLargeNumber } from 'component/common/PrettifyLargeNumber/PrettifyLargeNumber';
import { styled } from '@mui/material';
interface IFeatureOverviewEnvironmentMetrics { interface IFeatureOverviewEnvironmentMetrics {
environmentMetric?: IFeatureEnvironmentMetrics; environmentMetric?: IFeatureEnvironmentMetrics;
disabled?: boolean; disabled?: boolean;
} }
const StyledContainer = styled('div')({
marginLeft: 'auto',
display: 'flex',
alignItems: 'center',
});
const StyledInfo = styled('div')(({ theme }) => ({
marginRight: theme.spacing(1),
display: 'flex',
flexDirection: 'column',
}));
const StyledPercentage = styled('p')(({ theme }) => ({
color: theme.palette.primary.main,
textAlign: 'right',
fontSize: theme.fontSizes.bodySize,
}));
const StyledInfoParagraph = styled('p')(({ theme }) => ({
maxWidth: '270px',
marginTop: theme.spacing(0.5),
fontSize: theme.fontSizes.smallBody,
textAlign: 'right',
[theme.breakpoints.down(700)]: {
display: 'none',
},
}));
const StyledIcon = styled(FiberManualRecord)(({ theme }) => ({
fill: theme.palette.standaloneBackground,
height: '75px',
width: '75px',
[theme.breakpoints.down(500)]: {
display: 'none',
},
}));
const StyledPercentageCircle = styled('div')(({ theme }) => ({
margin: theme.spacing(0, 2),
[theme.breakpoints.down(500)]: {
display: 'none',
},
}));
const FeatureOverviewEnvironmentMetrics = ({ const FeatureOverviewEnvironmentMetrics = ({
environmentMetric, environmentMetric,
disabled = false, disabled = false,
}: IFeatureOverviewEnvironmentMetrics) => { }: IFeatureOverviewEnvironmentMetrics) => {
const { classes: styles } = useStyles();
const theme = useTheme(); const theme = useTheme();
if (!environmentMetric) return null; if (!environmentMetric) return null;
@ -28,10 +71,9 @@ const FeatureOverviewEnvironmentMetrics = ({
(environmentMetric.yes === 0 && environmentMetric.no === 0) (environmentMetric.yes === 0 && environmentMetric.no === 0)
) { ) {
return ( return (
<div className={styles.container}> <StyledContainer>
<div className={styles.info}> <StyledInfo>
<p <StyledPercentage
className={styles.percentage}
style={{ style={{
color: disabled color: disabled
? theme.palette.text.secondary ? theme.palette.text.secondary
@ -40,9 +82,8 @@ const FeatureOverviewEnvironmentMetrics = ({
data-loading data-loading
> >
{percentage}% {percentage}%
</p> </StyledPercentage>
<p <StyledInfoParagraph
className={styles.infoParagraph}
style={{ style={{
color: disabled color: disabled
? theme.palette.text.secondary ? theme.palette.text.secondary
@ -53,22 +94,18 @@ const FeatureOverviewEnvironmentMetrics = ({
The feature has been requested <b>0 times</b> and The feature has been requested <b>0 times</b> and
exposed exposed
<b> 0 times</b> in the last hour <b> 0 times</b> in the last hour
</p> </StyledInfoParagraph>
</div> </StyledInfo>
<FiberManualRecord <StyledIcon style={{ transform: 'scale(1.1)' }} data-loading />
className={styles.icon} </StyledContainer>
style={{ transform: 'scale(1.1)' }}
data-loading
/>
</div>
); );
} }
return ( return (
<div className={styles.container}> <StyledContainer>
<div className={styles.info}> <StyledInfo>
<p className={styles.percentage}>{percentage}%</p> <StyledPercentage>{percentage}%</StyledPercentage>
<p className={styles.infoParagraph}> <StyledInfoParagraph>
The feature has been requested{' '} The feature has been requested{' '}
<b> <b>
<PrettifyLargeNumber value={total} /> times <PrettifyLargeNumber value={total} /> times
@ -79,12 +116,12 @@ const FeatureOverviewEnvironmentMetrics = ({
times times
</b>{' '} </b>{' '}
in the last hour in the last hour
</p> </StyledInfoParagraph>
</div> </StyledInfo>
<div className={styles.percentageCircle} data-loading> <StyledPercentageCircle data-loading>
<PercentageCircle percentage={percentage} size="3rem" /> <PercentageCircle percentage={percentage} size="3rem" />
</div> </StyledPercentageCircle>
</div> </StyledContainer>
); );
}; };

View File

@ -1,10 +1,8 @@
import { capitalize } from '@mui/material'; import { capitalize, styled } from '@mui/material';
import classnames from 'classnames';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature'; import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons'; import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useStyles } from './FeatureOverviewMetadata.styles';
import { Edit } from '@mui/icons-material'; import { Edit } from '@mui/icons-material';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions'; import { UPDATE_FEATURE } from 'component/providers/AccessProvider/permissions';
@ -13,9 +11,62 @@ import FeatureOverviewTags from './FeatureOverviewTags/FeatureOverviewTags';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
const StyledContainer = styled('div')(({ theme }) => ({
borderRadius: theme.shape.borderRadiusLarge,
color: theme.palette.text.tertiaryContrast,
backgroundColor: theme.palette.featureMetaData,
display: 'flex',
flexDirection: 'column',
maxWidth: '350px',
minWidth: '350px',
marginRight: theme.spacing(2),
[theme.breakpoints.down(1000)]: {
width: '100%',
maxWidth: 'none',
minWidth: 'auto',
},
}));
const StyledPaddingContainerTop = styled('div')({
padding: '1.5rem 1.5rem 0 1.5rem',
});
const StyledPaddingContainerBottom = styled('div')(({ theme }) => ({
padding: '0 1.5rem 1.5rem 1.5rem',
borderTop: `1px solid ${theme.palette.divider}`,
}));
const StyledMetaDataHeader = styled('div')({
display: 'flex',
alignItems: 'center',
});
const StyledHeader = styled('h2')(({ theme }) => ({
fontSize: theme.fontSizes.bodySize,
fontWeight: 'normal',
margin: 0,
}));
const StyledBody = styled('div')(({ theme }) => ({
margin: theme.spacing(2, 0),
display: 'flex',
flexDirection: 'column',
}));
const StyledBodyItem = styled('span')(({ theme }) => ({
margin: theme.spacing(1, 0),
fontSize: theme.fontSizes.bodySize,
wordBreak: 'break-all',
}));
const StyledDescriptionContainer = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
color: theme.palette.text.tertiaryContrast,
}));
const FeatureOverviewMetaData = () => { const FeatureOverviewMetaData = () => {
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
const { classes: styles } = useStyles();
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId'); const featureId = useRequiredPathParam('featureId');
const { tags } = useTags(featureId); const { tags } = useTags(featureId);
@ -25,24 +76,29 @@ const FeatureOverviewMetaData = () => {
const IconComponent = getFeatureTypeIcons(type); const IconComponent = getFeatureTypeIcons(type);
return ( return (
<div className={classnames(styles.container)}> <StyledContainer>
<div className={styles.paddingContainerTop}> <StyledPaddingContainerTop>
<div className={styles.metaDataHeader} data-loading> <StyledMetaDataHeader data-loading>
<IconComponent className={styles.headerIcon} />{' '} <IconComponent
<h2 className={styles.header}> sx={theme => ({
{capitalize(type || '')} toggle marginRight: theme.spacing(2),
</h2> height: '40px',
</div> width: '40px',
<div className={styles.body}> fill: theme.palette.text.tertiaryContrast,
<span className={styles.bodyItem} data-loading> })}
/>{' '}
<StyledHeader>{capitalize(type || '')} toggle</StyledHeader>
</StyledMetaDataHeader>
<StyledBody>
<StyledBodyItem data-loading>
Project: {project} Project: {project}
</span> </StyledBodyItem>
<ConditionallyRender <ConditionallyRender
condition={Boolean(description)} condition={Boolean(description)}
show={ show={
<span className={styles.bodyItem} data-loading> <StyledBodyItem data-loading>
<div>Description:</div> <div>Description:</div>
<div className={styles.descriptionContainer}> <StyledDescriptionContainer>
<p>{description}</p> <p>{description}</p>
<PermissionIconButton <PermissionIconButton
projectId={projectId} projectId={projectId}
@ -53,14 +109,19 @@ const FeatureOverviewMetaData = () => {
title: 'Edit description', title: 'Edit description',
}} }}
> >
<Edit className={styles.editIcon} /> <Edit
sx={theme => ({
color: theme.palette.text
.tertiaryContrast,
})}
/>
</PermissionIconButton> </PermissionIconButton>
</div> </StyledDescriptionContainer>
</span> </StyledBodyItem>
} }
elseShow={ elseShow={
<span data-loading> <span data-loading>
<div className={styles.descriptionContainer}> <StyledDescriptionContainer>
No description.{' '} No description.{' '}
<PermissionIconButton <PermissionIconButton
projectId={projectId} projectId={projectId}
@ -71,26 +132,31 @@ const FeatureOverviewMetaData = () => {
title: 'Edit description', title: 'Edit description',
}} }}
> >
<Edit className={styles.editIcon} /> <Edit
sx={theme => ({
color: theme.palette.text
.tertiaryContrast,
})}
/>
</PermissionIconButton> </PermissionIconButton>
</div> </StyledDescriptionContainer>
</span> </span>
} }
/> />
</div> </StyledBody>
</div> </StyledPaddingContainerTop>
<ConditionallyRender <ConditionallyRender
condition={ condition={
tags.length > 0 && tags.length > 0 &&
!Boolean(uiConfig.flags.variantsPerEnvironment) !Boolean(uiConfig.flags.variantsPerEnvironment)
} }
show={ show={
<div className={styles.paddingContainerBottom}> <StyledPaddingContainerBottom>
<FeatureOverviewTags projectId={projectId} /> <FeatureOverviewTags projectId={projectId} />
</div> </StyledPaddingContainerBottom>
} }
/> />
</div> </StyledContainer>
); );
}; };

View File

@ -1,59 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
borderRadius: theme.shape.borderRadiusLarge,
color: '#fff',
backgroundColor: theme.palette.featureMetaData,
display: 'flex',
flexDirection: 'column',
maxWidth: '350px',
minWidth: '350px',
marginRight: '1rem',
[theme.breakpoints.down(1000)]: {
width: '100%',
maxWidth: 'none',
minWidth: 'auto',
},
},
paddingContainerTop: {
padding: '1.5rem 1.5rem 0 1.5rem',
},
paddingContainerBottom: {
padding: '0 1.5rem 1.5rem 1.5rem',
borderTop: `1px solid ${theme.palette.grey[300]}`,
},
metaDataHeader: {
display: 'flex',
alignItems: 'center',
},
header: {
fontSize: theme.fontSizes.bodySize,
fontWeight: 'normal',
margin: 0,
},
body: {
margin: '1rem 0',
display: 'flex',
flexDirection: 'column',
},
bodyItem: {
margin: '0.5rem 0',
fontSize: theme.fontSizes.bodySize,
wordBreak: 'break-all',
},
headerIcon: {
marginRight: '1rem',
height: '40px',
width: '40px',
fill: '#fff',
},
descriptionContainer: {
display: 'flex',
alignItems: 'center',
color: '#fff',
},
editIcon: {
color: '#fff',
},
}));

View File

@ -1,38 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
borderRadius: theme.shape.borderRadiusLarge,
backgroundColor: theme.palette.primary.main,
display: 'flex',
flexDirection: 'column',
marginRight: '1rem',
marginTop: '1rem',
[theme.breakpoints.down(800)]: {
width: '100%',
maxWidth: 'none',
},
},
tagHeader: {
display: 'flex',
alignItems: 'center',
},
tag: {
height: '40px',
width: '40px',
fill: theme.palette.primary.main,
marginRight: '0.8rem',
},
tagChip: {
marginRight: '0.25rem',
marginTop: '0.5rem',
backgroundColor: '#fff',
fontSize: theme.fontSizes.smallBody,
},
closeIcon: {
color: theme.palette.primary.light,
'&:hover': {
color: theme.palette.primary.light,
},
},
}));

View File

@ -1,8 +1,7 @@
import React, { useContext, useState } from 'react'; import React, { useContext, useState } from 'react';
import { Chip } from '@mui/material'; import { Chip, styled } from '@mui/material';
import { Close, Label } from '@mui/icons-material'; import { Close, Label } from '@mui/icons-material';
import useTags from 'hooks/api/getters/useTags/useTags'; import useTags from 'hooks/api/getters/useTags/useTags';
import { useStyles } from './FeatureOverviewTags.styles';
import slackIcon from 'assets/icons/slack.svg'; import slackIcon from 'assets/icons/slack.svg';
import jiraIcon from 'assets/icons/jira.svg'; import jiraIcon from 'assets/icons/jira.svg';
import webhookIcon from 'assets/icons/webhooks.svg'; import webhookIcon from 'assets/icons/webhooks.svg';
@ -18,10 +17,37 @@ import AccessContext from 'contexts/AccessContext';
import { formatUnknownError } from 'utils/formatUnknownError'; import { formatUnknownError } from 'utils/formatUnknownError';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
interface IFeatureOverviewTagsProps extends React.HTMLProps<HTMLDivElement> { interface IFeatureOverviewTagsProps {
projectId: string; projectId: string;
} }
const StyledContainer = styled('div')(({ theme }) => ({
borderRadius: theme.shape.borderRadiusLarge,
backgroundColor: theme.palette.primary.main,
display: 'flex',
flexDirection: 'column',
marginRight: theme.spacing(2),
marginTop: theme.spacing(2),
[theme.breakpoints.down(800)]: {
width: '100%',
maxWidth: 'none',
},
}));
const StyledTagChip = styled(Chip)(({ theme }) => ({
marginRight: theme.spacing(0.5),
marginTop: theme.spacing(1),
backgroundColor: theme.palette.text.tertiaryContrast,
fontSize: theme.fontSizes.smallBody,
}));
const StyledCloseIcon = styled(Close)(({ theme }) => ({
color: theme.palette.primary.light,
'&:hover': {
color: theme.palette.primary.light,
},
}));
const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({ const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({
projectId, projectId,
...rest ...rest
@ -31,7 +57,6 @@ const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({
value: '', value: '',
type: '', type: '',
}); });
const { classes: styles } = useStyles();
const featureId = useRequiredPathParam('featureId'); const featureId = useRequiredPathParam('featureId');
const { tags, refetch } = useTags(featureId); const { tags, refetch } = useTags(featureId);
const { tagTypes } = useTagTypes(); const { tagTypes } = useTagTypes();
@ -98,15 +123,12 @@ const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({
}; };
const renderTag = (t: ITag) => ( const renderTag = (t: ITag) => (
<Chip <StyledTagChip
icon={tagIcon(t.type)} icon={tagIcon(t.type)}
className={styles.tagChip}
data-loading data-loading
label={t.value} label={t.value}
key={`${t.type}:${t.value}`} key={`${t.type}:${t.value}`}
deleteIcon={ deleteIcon={<StyledCloseIcon titleAccess="Remove" />}
<Close className={styles.closeIcon} titleAccess="Remove" />
}
onDelete={ onDelete={
canDeleteTag canDeleteTag
? () => { ? () => {
@ -119,7 +141,7 @@ const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({
); );
return ( return (
<div className={styles.container} {...rest}> <StyledContainer {...rest}>
<Dialogue <Dialogue
open={showDelDialog} open={showDelDialog}
onClose={() => { onClose={() => {
@ -141,7 +163,7 @@ const FeatureOverviewTags: React.FC<IFeatureOverviewTagsProps> = ({
elseShow={<p data-loading>No tags to display</p>} elseShow={<p data-loading>No tags to display</p>}
/> />
</div> </div>
</div> </StyledContainer>
); );
}; };

View File

@ -1,30 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
innerContainer: {
display: 'flex',
},
bodyContainer: {
padding: 0,
},
listContainer: {
width: '20%',
borderRight: `1px solid ${theme.palette.grey[300]}`,
padding: '1rem 0',
[theme.breakpoints.down('md')]: {
width: '35%',
},
},
listItem: {
padding: '0.75rem 2rem',
},
innerBodyContainer: {
padding: '2rem',
display: 'flex',
flexDirection: 'column',
width: 400,
['& > *']: {
margin: '0.5rem 0',
},
},
}));

View File

@ -1,7 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { PageContent } from 'component/common/PageContent/PageContent'; import { PageContent } from 'component/common/PageContent/PageContent';
import { useStyles } from './FeatureSettings.styles'; import { Box, List, ListItem, styled } from '@mui/material';
import { List, ListItem } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import FeatureSettingsProject from './FeatureSettingsProject/FeatureSettingsProject'; import FeatureSettingsProject from './FeatureSettingsProject/FeatureSettingsProject';
import { FeatureSettingsInformation } from './FeatureSettingsInformation/FeatureSettingsInformation'; import { FeatureSettingsInformation } from './FeatureSettingsInformation/FeatureSettingsInformation';
@ -11,21 +10,39 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
const METADATA = 'metadata'; const METADATA = 'metadata';
const PROJECT = 'project'; const PROJECT = 'project';
const StyledListContainer = styled('div')(({ theme }) => ({
width: '20%',
borderRight: `1px solid ${theme.palette.divider}`,
padding: theme.spacing(2, 0),
[theme.breakpoints.down('md')]: {
width: '35%',
},
}));
const StyledInnerBodyContainer = styled('div')(({ theme }) => ({
padding: theme.spacing(4),
display: 'flex',
flexDirection: 'column',
width: 400,
['& > *']: {
margin: theme.spacing(1, 0),
},
}));
export const FeatureSettings = () => { export const FeatureSettings = () => {
const { classes: styles } = useStyles();
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId'); const featureId = useRequiredPathParam('featureId');
const [settings, setSettings] = useState(METADATA); const [settings, setSettings] = useState(METADATA);
const { uiConfig } = useUiConfig(); const { uiConfig } = useUiConfig();
return ( return (
<PageContent header="Settings" bodyClass={styles.bodyContainer}> <PageContent header="Settings" sx={{ padding: 0 }}>
<div className={styles.innerContainer}> <Box sx={{ display: 'flex' }}>
<div className={styles.listContainer}> <StyledListContainer>
<List> <List>
<ListItem <ListItem
key={0} key={0}
className={styles.listItem} sx={{ padding: '0.75rem 2rem' }}
button button
onClick={() => setSettings(METADATA)} onClick={() => setSettings(METADATA)}
selected={settings === METADATA} selected={settings === METADATA}
@ -34,7 +51,7 @@ export const FeatureSettings = () => {
</ListItem> </ListItem>
<ListItem <ListItem
key={1} key={1}
className={styles.listItem} sx={{ padding: '0.75rem 2rem' }}
button button
onClick={() => setSettings(PROJECT)} onClick={() => setSettings(PROJECT)}
selected={settings === PROJECT} selected={settings === PROJECT}
@ -43,8 +60,8 @@ export const FeatureSettings = () => {
Project Project
</ListItem> </ListItem>
</List> </List>
</div> </StyledListContainer>
<div className={styles.innerBodyContainer}> <StyledInnerBodyContainer>
<ConditionallyRender <ConditionallyRender
condition={settings === METADATA} condition={settings === METADATA}
show={ show={
@ -58,8 +75,8 @@ export const FeatureSettings = () => {
condition={settings === PROJECT && uiConfig.flags.P} condition={settings === PROJECT && uiConfig.flags.P}
show={<FeatureSettingsProject />} show={<FeatureSettingsProject />}
/> />
</div> </StyledInnerBodyContainer>
</div> </Box>
</PageContent> </PageContent>
); );
}; };