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

Makestyles refactor #7/1 (#2805)

This commit is contained in:
sjaanus 2023-01-03 16:30:59 +02:00 committed by GitHub
parent cc1512cd44
commit b631618532
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 641 additions and 823 deletions

View File

@ -7,7 +7,7 @@ import {
} from 'component/common/AutocompleteBox/AutocompleteBox';
import { FeatureStrategySegmentList } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegmentList';
import { useStyles } from 'component/feature/FeatureStrategy/FeatureStrategySegment/FeatureStrategySegment.styles';
import { SegmentDocsStrategyWarning } from 'component/segments/SegmentDocs/SegmentDocs';
import { SegmentDocsStrategyWarning } from 'component/segments/SegmentDocs';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';
import { Divider, Typography } from '@mui/material';

View File

@ -48,7 +48,7 @@ import { CreateSegment } from 'component/segments/CreateSegment/CreateSegment';
import { EditSegment } from 'component/segments/EditSegment/EditSegment';
import { IRoute } from 'interfaces/route';
import { EnvironmentTable } from 'component/environments/EnvironmentTable/EnvironmentTable';
import { SegmentTable } from 'component/segments/SegmentTable/SegmentTable';
import { SegmentTable } from 'component/segments/SegmentTable';
import FlaggedBillingRedirect from 'component/admin/billing/FlaggedBillingRedirect/FlaggedBillingRedirect';
import { FeaturesArchiveTable } from '../archive/FeaturesArchiveTable';
import { Billing } from 'component/admin/billing/Billing';

View File

@ -10,9 +10,9 @@ import useToast from 'hooks/useToast';
import { useNavigate } from 'react-router-dom';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useSegmentForm } from '../hooks/useSegmentForm';
import { SegmentForm } from '../SegmentForm/SegmentForm';
import { SegmentForm } from '../SegmentForm';
import { feedbackCESContext } from 'component/feedback/FeedbackCESContext/FeedbackCESContext';
import { segmentsDocsLink } from 'component/segments/SegmentDocs/SegmentDocs';
import { segmentsDocsLink } from 'component/segments/SegmentDocs';
import { useSegmentValuesCount } from 'component/segments/hooks/useSegmentValuesCount';
import { SEGMENT_CREATE_BTN_ID } from 'utils/testIds';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';

View File

@ -11,10 +11,10 @@ import React from 'react';
import { useNavigate } from 'react-router-dom';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useSegmentForm } from '../hooks/useSegmentForm';
import { SegmentForm } from '../SegmentForm/SegmentForm';
import { SegmentForm } from '../SegmentForm';
import { segmentsFormDescription } from 'component/segments/CreateSegment/CreateSegment';
import { UpdateButton } from 'component/common/UpdateButton/UpdateButton';
import { segmentsDocsLink } from 'component/segments/SegmentDocs/SegmentDocs';
import { segmentsDocsLink } from 'component/segments/SegmentDocs';
import { useSegmentValuesCount } from 'component/segments/hooks/useSegmentValuesCount';
import { SEGMENT_SAVE_BTN_ID } from 'utils/testIds';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';

View File

@ -0,0 +1,58 @@
import { styled, Typography } from '@mui/material';
import { Link } from 'react-router-dom';
import { CREATE_SEGMENT } from 'component/providers/AccessProvider/permissions';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import AccessContext from 'contexts/AccessContext';
import { useContext } from 'react';
const StyledDiv = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
margin: theme.spacing(6),
marginLeft: 'auto',
marginRight: 'auto',
}));
const StyledTypography = styled(Typography)(({ theme }) => ({
fontSize: theme.fontSizes.mainHeader,
marginBottom: theme.spacing(2.5),
}));
const StyledParagraph = styled('p')(({ theme }) => ({
fontSize: theme.fontSizes.smallBody,
color: theme.palette.text.secondary,
maxWidth: 515,
marginBottom: theme.spacing(2.5),
textAlign: 'center',
}));
const StyledLink = styled(Link)(({ theme }) => ({
textDecoration: 'none',
color: theme.palette.primary.main,
fontWeight: theme.fontWeight.bold,
}));
export const SegmentEmpty = () => {
const { hasAccess } = useContext(AccessContext);
return (
<StyledDiv>
<StyledTypography>No segments yet!</StyledTypography>
<StyledParagraph>
Segment makes it easy for you to define who should be exposed to
your feature. The segment is often a collection of constraints
and can be reused.
</StyledParagraph>
<ConditionallyRender
condition={hasAccess(CREATE_SEGMENT)}
show={
<StyledLink to="/segments/create">
Create your first segment
</StyledLink>
}
/>
</StyledDiv>
);
};

View File

@ -1,29 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
empty: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
margin: theme.spacing(6),
marginLeft: 'auto',
marginRight: 'auto',
},
title: {
fontSize: theme.fontSizes.mainHeader,
marginBottom: theme.spacing(2.5),
},
subtitle: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.text.secondary,
maxWidth: 515,
marginBottom: theme.spacing(2.5),
textAlign: 'center',
},
paramButton: {
textDecoration: 'none',
color: theme.palette.primary.main,
fontWeight: theme.fontWeight.bold,
},
}));

View File

@ -1,31 +0,0 @@
import { Typography } from '@mui/material';
import { useStyles } from 'component/segments/SegmentEmpty/SegmentEmpty.styles';
import { Link } from 'react-router-dom';
import { CREATE_SEGMENT } from 'component/providers/AccessProvider/permissions';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import AccessContext from 'contexts/AccessContext';
import { useContext } from 'react';
export const SegmentEmpty = () => {
const { classes } = useStyles();
const { hasAccess } = useContext(AccessContext);
return (
<div className={classes.empty}>
<Typography className={classes.title}>No segments yet!</Typography>
<p className={classes.subtitle}>
Segment makes it easy for you to define who should be exposed to
your feature. The segment is often a collection of constraints
and can be reused.
</p>
<ConditionallyRender
condition={hasAccess(CREATE_SEGMENT)}
show={
<Link to="/segments/create" className={classes.paramButton}>
Create your first segment
</Link>
}
/>
</div>
);
};

View File

@ -1,14 +1,24 @@
import { IConstraint } from 'interfaces/strategy';
import { useStyles } from './SegmentForm.styles';
import { SegmentFormStepOne } from '../SegmentFormStepOne/SegmentFormStepOne';
import { SegmentFormStepTwo } from '../SegmentFormStepTwo/SegmentFormStepTwo';
import { SegmentFormStepOne } from './SegmentFormStepOne';
import { SegmentFormStepTwo } from './SegmentFormStepTwo';
import React, { useState } from 'react';
import { SegmentFormStepList } from 'component/segments/SegmentFormStepList/SegmentFormStepList';
import { SegmentFormStepList } from 'component/segments/SegmentFormStepList';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { styled } from '@mui/material';
export type SegmentFormStep = 1 | 2;
export type SegmentFormMode = 'create' | 'edit';
const Styled = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
margin: theme.spacing(6),
marginLeft: 'auto',
marginRight: 'auto',
}));
interface ISegmentProps {
name: string;
description: string;
@ -22,6 +32,12 @@ interface ISegmentProps {
mode: SegmentFormMode;
}
const StyledForm = styled('form')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
height: '100%',
}));
export const SegmentForm: React.FC<ISegmentProps> = ({
children,
name,
@ -35,14 +51,13 @@ export const SegmentForm: React.FC<ISegmentProps> = ({
clearErrors,
mode,
}) => {
const { classes: styles } = useStyles();
const totalSteps = 2;
const [currentStep, setCurrentStep] = useState<SegmentFormStep>(1);
return (
<>
<SegmentFormStepList total={totalSteps} current={currentStep} />
<form onSubmit={handleSubmit} className={styles.form}>
<StyledForm onSubmit={handleSubmit}>
<ConditionallyRender
condition={currentStep === 1}
show={
@ -70,7 +85,7 @@ export const SegmentForm: React.FC<ISegmentProps> = ({
</SegmentFormStepTwo>
}
/>
</form>
</StyledForm>
</>
);
};

View File

@ -1,53 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
maxWidth: '400px',
},
form: {
display: 'flex',
flexDirection: 'column',
height: '100%',
},
input: { width: '100%', marginBottom: '1rem' },
label: {
minWidth: '300px',
[theme.breakpoints.down(600)]: {
minWidth: 'auto',
},
},
buttonContainer: {
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
},
cancelButton: {
marginLeft: '1.5rem',
},
inputDescription: {
marginBottom: '0.5rem',
},
formHeader: {
fontWeight: 'normal',
marginTop: '0',
},
header: {
fontWeight: 'normal',
},
errorMessage: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.error.main,
position: 'absolute',
top: '-8px',
},
userInfoContainer: {
margin: '-20px 0',
},
errorAlert: {
marginBottom: '1rem',
},
flexRow: {
display: 'flex',
alignItems: 'center',
},
}));

View File

@ -0,0 +1,72 @@
import { FiberManualRecord } from '@mui/icons-material';
import React from 'react';
import { styled } from '@mui/material';
import { formTemplateSidebarWidth } from '../common/FormTemplate/FormTemplate.styles';
interface ISegmentFormStepListProps {
total: number;
current: number;
}
const StyledContainer = styled('div')(({ theme }) => ({
display: 'flex',
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
top: 30,
left: 0,
right: formTemplateSidebarWidth,
[theme.breakpoints.down(1100)]: {
right: 0,
},
}));
const StyledSteps = styled('div')(({ theme }) => ({
position: 'relative',
borderRadius: 10,
background: theme.palette.background.paper,
padding: theme.spacing(1, 2.5),
margin: 'auto',
display: 'flex',
alignItems: 'center',
}));
const StyledSpan = styled('span')(({ theme }) => ({
marginRight: 15,
fontSize: theme.fontSizes.smallBody,
}));
const StyledFiberManualRecord = styled(FiberManualRecord, {
shouldForwardProp: prop => prop !== 'filled',
})<{ filled: boolean }>(({ theme, filled }) => ({
fill: theme.palette.primary.main,
transition: 'opacity 0.4s ease',
opacity: filled ? 1 : 0.4,
fontSize: filled ? 20 : 17,
}));
export const SegmentFormStepList: React.FC<ISegmentFormStepListProps> = ({
total,
current,
}) => {
// Create a list with all the step numbers, e.g. [1, 2, 3].
const steps: number[] = Array.from({ length: total }).map((_, i) => {
return i + 1;
});
return (
<StyledContainer>
<StyledSteps>
<StyledSpan>
Step {current} of {total}
</StyledSpan>
{steps.map(step => (
<StyledFiberManualRecord
key={step}
filled={step === current}
/>
))}
</StyledSteps>
</StyledContainer>
);
};

View File

@ -1,40 +0,0 @@
import { makeStyles } from 'tss-react/mui';
import { formTemplateSidebarWidth } from 'component/common/FormTemplate/FormTemplate.styles';
export const useStyles = makeStyles()(theme => ({
container: {
display: 'flex',
position: 'absolute',
alignItems: 'center',
justifyContent: 'center',
top: 30,
left: 0,
right: formTemplateSidebarWidth,
[theme.breakpoints.down(1100)]: {
right: 0,
},
},
steps: {
position: 'relative',
borderRadius: 10,
background: theme.palette.background.paper,
padding: '0.6rem 1.5rem',
margin: 'auto',
display: 'flex',
alignItems: 'center',
},
stepsText: {
marginRight: 15,
fontSize: theme.fontSizes.smallBody,
},
circle: {
fill: theme.palette.primary.main,
fontSize: 17,
opacity: 0.4,
transition: 'opacity 0.4s ease',
},
filledCircle: {
opacity: 1,
fontSize: 20,
},
}));

View File

@ -1,40 +0,0 @@
import { FiberManualRecord } from '@mui/icons-material';
import { useStyles } from './SegmentFormStepList.styles';
import React from 'react';
import classNames from 'classnames';
interface ISegmentFormStepListProps {
total: number;
current: number;
}
export const SegmentFormStepList: React.FC<ISegmentFormStepListProps> = ({
total,
current,
}) => {
const { classes: styles } = useStyles();
// Create a list with all the step numbers, e.g. [1, 2, 3].
const steps: number[] = Array.from({ length: total }).map((_, i) => {
return i + 1;
});
return (
<div className={styles.container}>
<div className={styles.steps}>
<span className={styles.stepsText}>
Step {current} of {total}
</span>
{steps.map(step => (
<FiberManualRecord
key={step}
className={classNames(
styles.circle,
step === current && styles.filledCircle
)}
/>
))}
</div>
</div>
);
};

View File

@ -1,9 +1,8 @@
import { Button } from '@mui/material';
import { Button, styled } from '@mui/material';
import Input from 'component/common/Input/Input';
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useStyles } from 'component/segments/SegmentFormStepOne/SegmentFormStepOne.styles';
import { SegmentFormStep } from '../SegmentForm/SegmentForm';
import { SegmentFormStep } from './SegmentForm';
import {
SEGMENT_NAME_ID,
SEGMENT_DESC_ID,
@ -20,6 +19,35 @@ interface ISegmentFormPartOneProps {
setCurrentStep: React.Dispatch<React.SetStateAction<SegmentFormStep>>;
}
const StyledForm = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
height: '100%',
}));
const StyledContainer = styled('div')(({ theme }) => ({
maxWidth: '400px',
}));
const StyledInputDescription = styled('p')(({ theme }) => ({
marginBottom: theme.spacing(1),
}));
const StyledInput = styled(Input)(({ theme }) => ({
width: '100%',
marginBottom: theme.spacing(2),
}));
const StyledButtonContainer = styled('div')(({ theme }) => ({
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
}));
const StyledCancelButton = styled(Button)(({ theme }) => ({
marginLeft: theme.spacing(3),
}));
export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
children,
name,
@ -31,16 +59,14 @@ export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
setCurrentStep,
}) => {
const navigate = useNavigate();
const { classes: styles } = useStyles();
return (
<div className={styles.form}>
<div className={styles.container}>
<p className={styles.inputDescription}>
<StyledForm>
<StyledContainer>
<StyledInputDescription>
What is the segment name?
</p>
<Input
className={styles.input}
</StyledInputDescription>
<StyledInput
label="Segment name"
value={name}
onChange={e => setName(e.target.value)}
@ -50,11 +76,10 @@ export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
required
data-testid={SEGMENT_NAME_ID}
/>
<p className={styles.inputDescription}>
<StyledInputDescription>
What is the segment description?
</p>
<Input
className={styles.input}
</StyledInputDescription>
<StyledInput
label="Description (optional)"
value={description}
onChange={e => setDescription(e.target.value)}
@ -62,8 +87,8 @@ export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
errorText={errors.description}
data-testid={SEGMENT_DESC_ID}
/>
</div>
<div className={styles.buttonContainer}>
</StyledContainer>
<StyledButtonContainer>
<Button
type="button"
variant="contained"
@ -74,16 +99,15 @@ export const SegmentFormStepOne: React.FC<ISegmentFormPartOneProps> = ({
>
Next
</Button>
<Button
<StyledCancelButton
type="button"
className={styles.cancelButton}
onClick={() => {
navigate('/segments');
}}
>
Cancel
</Button>
</div>
</div>
</StyledCancelButton>
</StyledButtonContainer>
</StyledForm>
);
};

View File

@ -1,49 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
maxWidth: '400px',
},
form: {
display: 'flex',
flexDirection: 'column',
height: '100%',
},
input: { width: '100%', marginBottom: '1rem' },
label: {
minWidth: '300px',
[theme.breakpoints.down(600)]: {
minWidth: 'auto',
},
},
buttonContainer: {
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
},
cancelButton: {
marginLeft: '1.5rem',
},
inputDescription: {
marginBottom: '0.5rem',
},
header: {
fontWeight: 'normal',
},
errorMessage: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.error.main,
position: 'absolute',
top: '-8px',
},
userInfoContainer: {
margin: '-20px 0',
},
errorAlert: {
marginBottom: '1rem',
},
flexRow: {
display: 'flex',
alignItems: 'center',
},
}));

View File

@ -1,5 +1,5 @@
import React, { useRef, useState, useContext } from 'react';
import { Button } from '@mui/material';
import { Button, styled } from '@mui/material';
import { Add } from '@mui/icons-material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
@ -13,12 +13,11 @@ import {
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
import { IConstraint } from 'interfaces/strategy';
import { useNavigate } from 'react-router-dom';
import { useStyles } from 'component/segments/SegmentFormStepTwo/SegmentFormStepTwo.styles';
import {
ConstraintAccordionList,
IConstraintAccordionListRef,
} from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
import { SegmentFormStep, SegmentFormMode } from '../SegmentForm/SegmentForm';
import { SegmentFormStep, SegmentFormMode } from './SegmentForm';
import {
AutocompleteBox,
IAutocompleteBoxOption,
@ -26,7 +25,7 @@ import {
import {
SegmentDocsValuesWarning,
SegmentDocsValuesError,
} from 'component/segments/SegmentDocs/SegmentDocs';
} from 'component/segments/SegmentDocs';
import { useSegmentValuesCount } from 'component/segments/hooks/useSegmentValuesCount';
import AccessContext from 'contexts/AccessContext';
import { useSegmentLimits } from 'hooks/api/getters/useSegmentLimits/useSegmentLimits';
@ -38,6 +37,67 @@ interface ISegmentFormPartTwoProps {
mode: SegmentFormMode;
}
const StyledForm = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
height: '100%',
}));
const StyledWarning = styled('div')(({ theme }) => ({
marginBottom: '1.5rem',
}));
const StyledInputDescription = styled('p')(({ theme }) => ({
marginBottom: '1rem',
}));
const StyledAddContextContainer = styled('div')(({ theme }) => ({
marginTop: '1rem',
borderBottom: `1px solid ${theme.palette.grey[300]}`,
paddingBottom: '2rem',
}));
const StyledError = styled('div')(({ theme }) => ({
marginTop: '1.5rem',
}));
const StyledNoConstraintText = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginTop: theme.spacing(12),
}));
const StyledSubtitle = styled('p')(({ theme }) => ({
fontSize: theme.fontSizes.bodySize,
color: theme.palette.tertiary.dark,
maxWidth: 515,
marginBottom: theme.spacing(2.5),
wordBreak: 'break-word',
whiteSpace: 'normal',
textAlign: 'center',
}));
const StyledConstraintContainer = styled('div')(({ theme }) => ({
marginBlock: theme.spacing(4),
}));
const StyledButtonContainer = styled('div')(({ theme }) => ({
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
borderTop: `1px solid ${theme.palette.tertiary.contrast}`,
paddingTop: theme.spacing(2),
}));
const StyledBackButton = styled(Button)(({ theme }) => ({
marginRight: 'auto',
}));
const StyledCancelButton = styled(Button)(({ theme }) => ({
marginLeft: theme.spacing(3),
}));
export const SegmentFormStepTwo: React.FC<ISegmentFormPartTwoProps> = ({
children,
constraints,
@ -48,7 +108,6 @@ export const SegmentFormStepTwo: React.FC<ISegmentFormPartTwoProps> = ({
const constraintsAccordionListRef = useRef<IConstraintAccordionListRef>();
const navigate = useNavigate();
const { hasAccess } = useContext(AccessContext);
const { classes: styles } = useStyles();
const { context = [] } = useUnleashContext();
const [open, setOpen] = useState(false);
const segmentValuesCount = useSegmentValuesCount(constraints);
@ -70,28 +129,28 @@ export const SegmentFormStepTwo: React.FC<ISegmentFormPartTwoProps> = ({
return (
<>
<div className={styles.form}>
<div className={styles.warning}>
<StyledForm>
<StyledWarning>
<SegmentDocsValuesWarning />
</div>
</StyledWarning>
<div>
<p className={styles.inputDescription}>
<StyledInputDescription>
Select the context fields you want to include in the
segment.
</p>
<p className={styles.inputDescription}>
</StyledInputDescription>
<StyledInputDescription>
Use a predefined context field:
</p>
</StyledInputDescription>
<AutocompleteBox
label="Select a context"
options={autocompleteOptions}
onChange={onChange}
/>
</div>
<div className={styles.addContextContainer}>
<p className={styles.inputDescription}>
<StyledAddContextContainer>
<StyledInputDescription>
...or add a new context field:
</p>
</StyledInputDescription>
<SidebarModal
label="Create new context"
onClose={() => setOpen(false)}
@ -113,26 +172,26 @@ export const SegmentFormStepTwo: React.FC<ISegmentFormPartTwoProps> = ({
Add context field
</PermissionButton>
{overSegmentValuesLimit && (
<div className={styles.error}>
<StyledError>
<SegmentDocsValuesError
values={segmentValuesCount}
/>
</div>
</StyledError>
)}
</div>
</StyledAddContextContainer>
<ConditionallyRender
condition={constraints.length === 0}
show={
<div className={styles.noConstraintText}>
<p className={styles.subtitle}>
<StyledNoConstraintText>
<StyledSubtitle>
Start adding context fields by selecting an
option from above, or you can create a new
context field and use it right away
</p>
</div>
</StyledSubtitle>
</StyledNoConstraintText>
}
/>
<div className={styles.constraintContainer}>
<StyledConstraintContainer>
<ConstraintAccordionList
ref={constraintsAccordionListRef}
constraints={constraints}
@ -142,27 +201,25 @@ export const SegmentFormStepTwo: React.FC<ISegmentFormPartTwoProps> = ({
: undefined
}
/>
</div>
</div>
<div className={styles.buttonContainer}>
<Button
</StyledConstraintContainer>
</StyledForm>
<StyledButtonContainer>
<StyledBackButton
type="button"
onClick={() => setCurrentStep(1)}
className={styles.backButton}
>
Back
</Button>
</StyledBackButton>
{children}
<Button
<StyledCancelButton
type="button"
className={styles.cancelButton}
onClick={() => {
navigate('/segments');
}}
>
Cancel
</Button>
</div>
</StyledCancelButton>
</StyledButtonContainer>
</>
);
};

View File

@ -1,102 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
warning: {
marginBottom: '1.5rem',
},
error: {
marginTop: '1.5rem',
},
form: {
display: 'flex',
flexDirection: 'column',
height: '100%',
},
input: { width: '100%', marginBottom: '1rem' },
label: {
minWidth: '300px',
[theme.breakpoints.down(600)]: {
minWidth: 'auto',
},
},
buttonContainer: {
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
borderTop: `1px solid ${theme.palette.grey[300]}`,
paddingTop: 15,
},
errorsContainer: {
marginTop: '1rem',
},
cancelButton: {
marginLeft: '1.5rem',
},
inputDescription: {
marginBottom: '1rem',
},
formHeader: {
fontWeight: 'normal',
marginTop: '0',
},
header: {
fontWeight: 'normal',
},
errorMessage: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.error.main,
position: 'absolute',
top: '-8px',
},
userInfoContainer: {
margin: '-20px 0',
},
errorAlert: {
marginBottom: '1rem',
},
flexRow: {
display: 'flex',
alignItems: 'center',
},
backButton: {
marginRight: 'auto',
},
addContextContainer: {
marginTop: '1rem',
borderBottom: `1px solid ${theme.palette.grey[300]}`,
paddingBottom: '2rem',
},
addContextButton: {
color: theme.palette.primary.dark,
background: 'transparent',
boxShadow: 'none',
border: '1px solid',
'&:hover': {
background: 'transparent',
boxShadow: 'none',
},
},
divider: {
borderStyle: 'solid',
borderColor: `${theme.palette.grey[300]}`,
marginTop: '1rem !important',
},
noConstraintText: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginTop: '6rem',
},
subtitle: {
fontSize: theme.fontSizes.bodySize,
color: theme.palette.grey[600],
maxWidth: 515,
marginBottom: 20,
wordBreak: 'break-word',
whiteSpace: 'normal',
textAlign: 'center',
},
constraintContainer: {
marginBlock: '2rem',
},
}));

View File

@ -15,7 +15,7 @@ import { useMediaQuery } from '@mui/material';
import { sortTypes } from 'utils/sortTypes';
import { useSegments } from 'hooks/api/getters/useSegments/useSegments';
import { useMemo, useState } from 'react';
import { SegmentEmpty } from 'component/segments/SegmentEmpty/SegmentEmpty';
import { SegmentEmpty } from 'component/segments/SegmentEmpty';
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
import { DonutLarge } from '@mui/icons-material';
import { SegmentActionCell } from 'component/segments/SegmentActionCell/SegmentActionCell';

View File

@ -1,7 +1,7 @@
import { useNavigate, Navigate } from 'react-router-dom';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import useSplashApi from 'hooks/api/actions/useSplashApi/useSplashApi';
import { SplashPageOperators } from 'component/splash/SplashPageOperators/SplashPageOperators';
import { SplashPageOperators } from 'component/splash/SplashPageOperators';
import { useEffect } from 'react';
import { useAuthSplash } from 'hooks/api/getters/useAuth/useAuthSplash';
import { splashIds, SplashId } from 'component/splash/splash';

View File

@ -0,0 +1,170 @@
import { Link, useNavigate } from 'react-router-dom';
import { Button, IconButton, styled } from '@mui/material';
import { CloseOutlined } from '@mui/icons-material';
import { OperatorUpgradeAlert } from 'component/common/OperatorUpgradeAlert/OperatorUpgradeAlert';
const StyledContainer = styled('section')(({ theme }) => ({
backgroundColor: theme.palette.primary.light,
minHeight: '100vh',
padding: theme.spacing(2),
display: 'grid',
gap: theme.spacing(2),
alignItems: 'center',
alignContent: 'center',
justifyContent: 'center',
gridTemplateColumns: 'minmax(0,auto)',
fontWeight: theme.fontWeight.thin,
}));
const StyledContent = styled('div')(({ theme }) => ({
position: 'relative',
padding: theme.spacing(4),
borderRadius: theme.spacing(1),
backgroundColor: theme.palette.primary.main,
color: 'white',
[theme.breakpoints.up('md')]: {
padding: theme.spacing(8),
},
}));
const StyledHeader = styled('header')(({ theme }) => ({
textAlign: 'center',
}));
const StyledTitle = styled('h1')(({ theme }) => ({
fontWeight: 'inherit',
}));
const StyledCloseButton = styled(IconButton)(({ theme }) => ({
position: 'absolute',
top: 0,
right: 0,
color: 'inherit',
}));
const StyledIngress = styled('p')(({ theme }) => ({
maxWidth: theme.spacing(64),
margin: theme.spacing(3, 'auto', 0, 'auto'),
}));
const StyledBody = styled('div')(({ theme }) => ({
margin: theme.spacing(4, 0),
padding: theme.spacing(4, 0),
borderTop: '1px solid',
borderBottom: '1px solid',
borderTopColor: theme.palette.primary.light,
borderBottomColor: theme.palette.primary.light,
}));
const StyledList = styled('ul')(({ theme }) => ({
padding: theme.spacing(2, 0),
[theme.breakpoints.up('md')]: {
padding: theme.spacing(2, 4),
},
'& li + li': {
marginTop: theme.spacing(0.5),
},
'& strong': {
padding: theme.spacing(0, 0.5),
fontSize: theme.fontSizes.smallBody,
fontWeight: 'inherit',
backgroundColor: 'rgba(0, 0, 0, 0.2)',
},
}));
const StyledFooter = styled('footer')(({ theme }) => ({
display: 'grid',
gap: theme.spacing(4),
textAlign: 'center',
justifyItems: 'center',
}));
const StyledLink = styled('a')(({ theme }) => ({
color: 'inherit',
}));
export const SplashPageOperators = () => {
const navigate = useNavigate();
return (
<StyledContainer>
<StyledContent>
<StyledHeader>
<StyledTitle>New strategy operators</StyledTitle>
<StyledCloseButton
onClick={() => navigate('/')}
size="large"
>
<CloseOutlined titleAccess="Close" />
</StyledCloseButton>
<StyledIngress>
We've added some new feature strategy constraint
operators. Fine-tune your feature targeting like never
before.
</StyledIngress>
</StyledHeader>
<StyledBody>
<p>For example:</p>
<StyledList>
<li>
<span>Toggle features at dates: </span>
<span>
<strong>DATE_BEFORE</strong>{' '}
<strong>DATE_AFTER</strong>
</span>
</li>
<li>
<span>Toggle features for versions: </span>
<span>
<strong>SEMVER_EQ</strong>{' '}
<strong>SEMVER_GT</strong>{' '}
<strong>SEMVER_LT</strong>
</span>
</li>
<li>
<span>Toggle features for strings: </span>
<span>
<strong>STR_CONTAINS</strong>{' '}
<strong>STR_ENDS_WITH</strong>{' '}
<strong>STR_STARTS_WITH</strong>
</span>
</li>
<li>
<span>Toggle features for numbers: </span>
<span>
<strong>NUM_GT</strong> <strong>NUM_GTE</strong>{' '}
<strong>NUM_LT</strong> <strong>NUM_LTE</strong>
</span>
</li>
</StyledList>
</StyledBody>
<StyledFooter>
<p>
<StyledLink
href="https://docs.getunleash.io/reference/strategy-constraints#numeric-operators"
target="_blank"
rel="noreferrer"
>
Read all about operators in our in-depth{' '}
<strong>docs</strong>
</StyledLink>
.
</p>
<p>
<Button
sx={theme => ({
background: 'white !important',
color: theme.palette.primary.main,
})}
variant="contained"
component={Link}
to="/"
>
Fine, whatever, I have work to do!
</Button>
</p>
</StyledFooter>
</StyledContent>
<OperatorUpgradeAlert />
</StyledContainer>
);
};

View File

@ -1,78 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
backgroundColor: theme.palette.primary.light,
minHeight: '100vh',
padding: '1rem',
display: 'grid',
gap: '1rem',
alignItems: 'center',
alignContent: 'center',
justifyContent: 'center',
gridTemplateColumns: 'minmax(0,auto)',
fontWeight: theme.fontWeight.thin,
},
content: {
position: 'relative',
padding: '2rem',
borderRadius: '0.5rem',
backgroundColor: theme.palette.primary.main,
color: 'white',
[theme.breakpoints.up('md')]: {
padding: '4rem',
},
},
header: {
textAlign: 'center',
},
footer: {
display: 'grid',
gap: '2rem',
textAlign: 'center',
justifyItems: 'center',
},
body: {
margin: '2rem 0',
padding: '2rem 0',
borderTop: '1px solid',
borderBottom: '1px solid',
borderTopColor: theme.palette.primary.light,
borderBottomColor: theme.palette.primary.light,
},
close: {
position: 'absolute',
top: 0,
right: 0,
color: 'inherit',
},
title: {
fontWeight: 'inherit',
},
ingress: {
maxWidth: '32rem',
margin: '1.5rem auto 0 auto',
},
list: {
padding: '1rem 0',
[theme.breakpoints.up('md')]: {
padding: '1rem 2rem',
},
'& li + li': {
marginTop: '0.25rem',
},
'& strong': {
padding: '0 .2rem',
fontSize: theme.fontSizes.smallBody,
fontWeight: 'inherit',
backgroundColor: 'rgba(0, 0, 0, 0.2)',
},
},
link: {
color: 'inherit',
},
button: {
background: 'white !important',
color: theme.palette.primary.main,
},
}));

View File

@ -1,92 +0,0 @@
import { useStyles } from 'component/splash/SplashPageOperators/SplashPageOperators.styles';
import { Link, useNavigate } from 'react-router-dom';
import { Button, IconButton } from '@mui/material';
import { CloseOutlined } from '@mui/icons-material';
import { OperatorUpgradeAlert } from 'component/common/OperatorUpgradeAlert/OperatorUpgradeAlert';
export const SplashPageOperators = () => {
const navigate = useNavigate();
const { classes: styles } = useStyles();
return (
<section className={styles.container}>
<div className={styles.content}>
<header className={styles.header}>
<h1 className={styles.title}>New strategy operators</h1>
<IconButton
className={styles.close}
onClick={() => navigate('/')}
size="large"
>
<CloseOutlined titleAccess="Close" />
</IconButton>
<p className={styles.ingress}>
We've added some new feature strategy constraint
operators. Fine-tune your feature targeting like never
before.
</p>
</header>
<div className={styles.body}>
<p>For example:</p>
<ul className={styles.list}>
<li>
<span>Toggle features at dates: </span>
<span>
<strong>DATE_BEFORE</strong>{' '}
<strong>DATE_AFTER</strong>
</span>
</li>
<li>
<span>Toggle features for versions: </span>
<span>
<strong>SEMVER_EQ</strong>{' '}
<strong>SEMVER_GT</strong>{' '}
<strong>SEMVER_LT</strong>
</span>
</li>
<li>
<span>Toggle features for strings: </span>
<span>
<strong>STR_CONTAINS</strong>{' '}
<strong>STR_ENDS_WITH</strong>{' '}
<strong>STR_STARTS_WITH</strong>
</span>
</li>
<li>
<span>Toggle features for numbers: </span>
<span>
<strong>NUM_GT</strong> <strong>NUM_GTE</strong>{' '}
<strong>NUM_LT</strong> <strong>NUM_LTE</strong>
</span>
</li>
</ul>
</div>
<footer className={styles.footer}>
<p>
<a
href="https://docs.getunleash.io/reference/strategy-constraints#numeric-operators"
target="_blank"
rel="noreferrer"
className={styles.link}
>
Read all about operators in our in-depth{' '}
<strong>docs</strong>
</a>
.
</p>
<p>
<Button
className={styles.button}
variant="contained"
component={Link}
to="/"
>
Fine, whatever, I have work to do!
</Button>
</p>
</footer>
</div>
<OperatorUpgradeAlert />
</section>
);
};

View File

@ -1,61 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
maxWidth: 400,
},
form: {
display: 'flex',
flexDirection: 'column',
height: '100%',
},
input: { width: '100%', marginBottom: '1rem' },
selectInput: {
marginBottom: '1rem',
minWidth: '400px',
[theme.breakpoints.down(600)]: {
minWidth: '379px',
},
},
link: {
color: theme.palette.primary.light,
},
label: {
minWidth: '300px',
[theme.breakpoints.down(600)]: {
minWidth: 'auto',
},
},
buttonContainer: {
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
},
cancelButton: {
marginLeft: '1.5rem',
},
inputDescription: {
marginBottom: '0.5rem',
},
typeDescription: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.grey[600],
top: '-13px',
position: 'relative',
},
errorMessage: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.error.main,
position: 'absolute',
top: '-8px',
},
flexRow: {
display: 'flex',
alignItems: 'center',
marginTop: '0.5rem',
},
paramButton: {
color: theme.palette.primary.dark,
},
}));

View File

@ -1,6 +1,5 @@
import Input from 'component/common/Input/Input';
import { Button } from '@mui/material';
import { useStyles } from './StrategyForm.styles';
import { Button, styled } from '@mui/material';
import { Add } from '@mui/icons-material';
import { trim } from 'component/common/util';
import { StrategyParameters } from './StrategyParameters/StrategyParameters';
@ -23,6 +22,39 @@ interface IStrategyFormProps {
setErrors: React.Dispatch<React.SetStateAction<Record<string, string>>>;
}
const StyledForm = styled('form')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
height: '100%',
}));
const StyledContainer = styled('div')(({ theme }) => ({
maxWidth: 400,
}));
const StyledInputDescription = styled('p')(({ theme }) => ({
marginBottom: theme.spacing(1),
}));
const StyledInput = styled(Input)(({ theme }) => ({
width: '100%',
marginBottom: theme.spacing(2),
}));
const StyledParamButton = styled(Button)(({ theme }) => ({
color: theme.palette.primary.dark,
}));
const StyledButtonContainer = styled('div')(({ theme }) => ({
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
}));
const StyledCancelButton = styled(Button)(({ theme }) => ({
marginLeft: theme.spacing(3),
}));
export const StrategyForm: React.FC<IStrategyFormProps> = ({
children,
handleSubmit,
@ -38,7 +70,6 @@ export const StrategyForm: React.FC<IStrategyFormProps> = ({
mode,
clearErrors,
}) => {
const { classes: styles } = useStyles();
const updateParameter = (index: number, updated: object) => {
let item = { ...params[index] };
params[index] = Object.assign({}, item, updated);
@ -53,15 +84,14 @@ export const StrategyForm: React.FC<IStrategyFormProps> = ({
};
return (
<form onSubmit={handleSubmit} className={styles.form}>
<div className={styles.container}>
<p className={styles.inputDescription}>
<StyledForm onSubmit={handleSubmit}>
<StyledContainer>
<StyledInputDescription>
What would you like to call your strategy?
</p>
<Input
</StyledInputDescription>
<StyledInput
disabled={mode === 'Edit'}
autoFocus
className={styles.input}
label="Strategy name*"
value={strategyName}
onChange={e => setStrategyName(trim(e.target.value))}
@ -70,11 +100,10 @@ export const StrategyForm: React.FC<IStrategyFormProps> = ({
onFocus={clearErrors}
onBlur={validateStrategyName}
/>
<p className={styles.inputDescription}>
<StyledInputDescription>
What is your strategy description?
</p>
<Input
className={styles.input}
</StyledInputDescription>
<StyledInput
label="Strategy description"
value={strategyDesc}
onChange={e => setStrategyDesc(e.target.value)}
@ -88,29 +117,24 @@ export const StrategyForm: React.FC<IStrategyFormProps> = ({
setParams={setParams}
errors={errors}
/>
<Button
<StyledParamButton
onClick={e => {
e.preventDefault();
appParameter();
}}
variant="outlined"
color="secondary"
className={styles.paramButton}
startIcon={<Add />}
>
Add parameter
</Button>
</div>
<div className={styles.buttonContainer}>
</StyledParamButton>
</StyledContainer>
<StyledButtonContainer>
{children}
<Button
type="button"
onClick={handleCancel}
className={styles.cancelButton}
>
<StyledCancelButton type="button" onClick={handleCancel}>
Cancel
</Button>
</div>
</form>
</StyledCancelButton>
</StyledButtonContainer>
</StyledForm>
);
};

View File

@ -1,46 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
paramsContainer: {
maxWidth: '400px',
margin: '1rem 0',
},
divider: {
borderStyle: 'dashed',
margin: '1rem 0 1.5rem 0',
borderColor: theme.palette.grey[500],
},
nameContainer: {
display: 'flex',
alignItems: 'center',
marginBottom: '1rem',
},
name: {
minWidth: '365px',
width: '100%',
},
input: {
minWidth: '365px',
width: '100%',
marginBottom: '1rem',
},
description: {
minWidth: '365px',
marginBottom: '1rem',
},
checkboxLabel: {
marginTop: '-0.5rem',
},
inputDescription: {
marginBottom: '0.5rem',
},
errorMessage: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.error.main,
position: 'absolute',
top: '-8px',
},
paramButton: {
color: theme.palette.primary.dark,
},
}));

View File

@ -3,10 +3,10 @@ import {
Divider,
FormControlLabel,
IconButton,
styled,
Tooltip,
} from '@mui/material';
import { Delete } from '@mui/icons-material';
import { useStyles } from './StrategyParameter.styles';
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
import Input from 'component/common/Input/Input';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
@ -45,6 +45,49 @@ interface IStrategyParameterProps {
errors: { [key: string]: string };
}
const StyledParamsContainer = styled('div')(({ theme }) => ({
maxWidth: '400px',
margin: theme.spacing(2, 0),
}));
const StyledDivider = styled(Divider)(({ theme }) => ({
borderStyle: 'dashed',
margin: theme.spacing(2, 0, 3, 0),
borderColor: theme.palette.neutral.border,
}));
const StyledParagraph = styled('p')(({ theme }) => ({
minWidth: '365px',
width: '100%',
marginBottom: theme.spacing(2),
}));
const StyledNameContainer = styled('div')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
marginBottom: theme.spacing(2),
}));
const StyledNameInput = styled(Input)(({ theme }) => ({
minWidth: '365px',
width: '100%',
}));
const StyledGeneralSelect = styled(GeneralSelect)(({ theme }) => ({
minWidth: '365px',
width: '100%',
marginBottom: theme.spacing(2),
}));
const StyledDescriptionInput = styled(Input)(({ theme }) => ({
minWidth: '365px',
marginBottom: theme.spacing(2),
}));
const StyledFormControlLabel = styled(FormControlLabel)(({ theme }) => ({
marginTop: theme.spacing(-1),
}));
export const StrategyParameter = ({
set,
input,
@ -53,19 +96,17 @@ export const StrategyParameter = ({
setParams,
errors,
}: IStrategyParameterProps) => {
const { classes: styles } = useStyles();
const onTypeChange = (type: string) => {
set({ type });
};
return (
<div className={styles.paramsContainer}>
<Divider className={styles.divider} />
<StyledParamsContainer>
<StyledDivider />
<ConditionallyRender
condition={index === 0}
show={
<p className={styles.input}>
<StyledParagraph>
Parameters let you provide arguments to your strategy
that it can access for evaluation. Read more in the{' '}
<a
@ -76,16 +117,15 @@ export const StrategyParameter = ({
parameter types documentation
</a>
.
</p>
</StyledParagraph>
}
/>
<div className={styles.nameContainer}>
<Input
<StyledNameContainer>
<StyledNameInput
autoFocus
label={`Parameter name ${index + 1}*`}
onChange={e => set({ name: e.target.value })}
value={input.name}
className={styles.name}
error={Boolean(errors?.[`paramName${index}`])}
errorText={errors?.[`paramName${index}`]}
/>
@ -99,25 +139,23 @@ export const StrategyParameter = ({
<Delete />
</IconButton>
</Tooltip>
</div>
<GeneralSelect
</StyledNameContainer>
<StyledGeneralSelect
label="Type*"
name="type"
options={paramTypesOptions}
value={input.type}
onChange={onTypeChange}
id={`prop-type-${index}-select`}
className={styles.input}
/>
<Input
<StyledDescriptionInput
rows={2}
multiline
label={`Parameter name ${index + 1} description`}
onChange={({ target }) => set({ description: target.value })}
value={input.description}
className={styles.description}
/>
<FormControlLabel
<StyledFormControlLabel
control={
<Checkbox
checked={Boolean(input.required)}
@ -125,8 +163,7 @@ export const StrategyParameter = ({
/>
}
label="Required"
className={styles.checkboxLabel}
/>
</div>
</StyledParamsContainer>
);
};

View File

@ -1,39 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
maxWidth: '400px',
},
form: {
display: 'flex',
flexDirection: 'column',
height: '100%',
},
input: { width: '100%', marginBottom: '1rem' },
label: {
minWidth: '300px',
[theme.breakpoints.down(600)]: {
minWidth: 'auto',
},
},
buttonContainer: {
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
},
cancelButton: {
marginLeft: '1.5rem',
},
inputDescription: {
marginBottom: '0.5rem',
},
permissionErrorContainer: {
position: 'relative',
},
errorMessage: {
fontSize: theme.fontSizes.smallBody,
color: theme.palette.error.main,
position: 'absolute',
top: '-8px',
},
}));

View File

@ -1,7 +1,6 @@
import Input from 'component/common/Input/Input';
import { TextField, Button } from '@mui/material';
import { TextField, Button, styled } from '@mui/material';
import { useStyles } from './TagTypeForm.styles';
import React from 'react';
import { trim } from 'component/common/util';
import { EDIT } from 'constants/misc';
@ -19,6 +18,40 @@ interface ITagTypeForm {
validateNameUniqueness?: () => void;
}
const StyledForm = styled('form')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
height: '100%',
}));
const StyledContainer = styled('div')(({ theme }) => ({
maxWidth: '400px',
}));
const StyledInputDescription = styled('p')(({ theme }) => ({
marginBottom: theme.spacing(1),
}));
const StyledInput = styled(Input)(({ theme }) => ({
width: '100%',
marginBottom: theme.spacing(2),
}));
const StyledTextField = styled(TextField)(({ theme }) => ({
width: '100%',
marginBottom: theme.spacing(2),
}));
const StyledButtonContainer = styled('div')(({ theme }) => ({
marginTop: 'auto',
display: 'flex',
justifyContent: 'flex-end',
}));
const StyledCancelButton = styled(Button)(({ theme }) => ({
marginLeft: theme.spacing(3),
}));
const TagTypeForm: React.FC<ITagTypeForm> = ({
children,
handleSubmit,
@ -32,16 +65,13 @@ const TagTypeForm: React.FC<ITagTypeForm> = ({
validateNameUniqueness,
clearErrors,
}) => {
const { classes: styles } = useStyles();
return (
<form onSubmit={handleSubmit} className={styles.form}>
<div className={styles.container}>
<p className={styles.inputDescription}>
<StyledForm onSubmit={handleSubmit}>
<StyledContainer>
<StyledInputDescription>
What is your tag name?
</p>
<Input
className={styles.input}
</StyledInputDescription>
<StyledInput
label="Tag name"
value={tagName}
onChange={e => setTagName(trim(e.target.value))}
@ -53,9 +83,10 @@ const TagTypeForm: React.FC<ITagTypeForm> = ({
autoFocus
/>
<p className={styles.inputDescription}>What is this tag for?</p>
<TextField
className={styles.input}
<StyledInputDescription>
What is this tag for?
</StyledInputDescription>
<StyledTextField
label="Tag description"
variant="outlined"
multiline
@ -63,14 +94,14 @@ const TagTypeForm: React.FC<ITagTypeForm> = ({
value={tagDesc}
onChange={e => setTagDesc(e.target.value)}
/>
</div>
<div className={styles.buttonContainer}>
</StyledContainer>
<StyledButtonContainer>
{children}
<Button onClick={handleCancel} className={styles.cancelButton}>
<StyledCancelButton onClick={handleCancel}>
Cancel
</Button>
</div>
</form>
</StyledCancelButton>
</StyledButtonContainer>
</StyledForm>
);
};

View File

@ -1,14 +0,0 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
display: 'flex',
flexDirection: 'column',
position: 'relative',
},
button: {
width: '150px',
margin: '1rem auto',
display: 'block',
},
}));

View File

@ -1,20 +1,31 @@
import { Button } from '@mui/material';
import classnames from 'classnames';
import { Button, styled } from '@mui/material';
import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react';
import { useThemeStyles } from 'themes/themeStyles';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import PasswordChecker from './PasswordChecker/PasswordChecker';
import PasswordMatcher from './PasswordMatcher/PasswordMatcher';
import { useStyles } from './ResetPasswordForm.styles';
import PasswordField from 'component/common/PasswordField/PasswordField';
interface IResetPasswordProps {
onSubmit: (password: string) => void;
}
const StyledForm = styled('form')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
position: 'relative',
'& > *': {
marginTop: `${theme.spacing(1)} !important`,
marginBottom: `${theme.spacing(1)} !important`,
},
}));
const StyledButton = styled(Button)(({ theme }) => ({
width: '150px',
margin: theme.spacing(2, 'auto'),
display: 'block',
}));
const ResetPasswordForm = ({ onSubmit }: IResetPasswordProps) => {
const { classes: styles } = useStyles();
const { classes: themeStyles } = useThemeStyles();
const [password, setPassword] = useState('');
const [showPasswordChecker, setShowPasswordChecker] = useState(false);
const [confirmPassword, setConfirmPassword] = useState('');
@ -50,13 +61,7 @@ const ResetPasswordForm = ({ onSubmit }: IResetPasswordProps) => {
const started = Boolean(password && confirmPassword);
return (
<form
onSubmit={handleSubmit}
className={classnames(
themeStyles.contentSpacingY,
styles.container
)}
>
<StyledForm onSubmit={handleSubmit}>
<PasswordField
placeholder="Password"
value={password || ''}
@ -91,17 +96,16 @@ const ResetPasswordForm = ({ onSubmit }: IResetPasswordProps) => {
started={started}
matchingPasswords={matchingPasswords}
/>
<Button
<StyledButton
variant="contained"
color="primary"
type="submit"
className={styles.button}
data-loading
disabled={!submittable}
>
Submit
</Button>
</form>
</StyledButton>
</StyledForm>
);
};