Skip to content

Commit

Permalink
Feature overview sidepanel UI improvements (#2502)
Browse files Browse the repository at this point in the history
  • Loading branch information
nunogois committed Nov 22, 2022
1 parent 801df69 commit 4d5c12d
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Typography } from '@mui/material';
import { styled, Typography } from '@mui/material';
import React, { useState } from 'react';
import { Dialogue } from 'component/common/Dialogue/Dialogue';
import Input from 'component/common/Input/Input';
Expand All @@ -10,6 +10,11 @@ import useTags from 'hooks/api/getters/useTags/useTags';
import useToast from 'hooks/useToast';
import { formatUnknownError } from 'utils/formatUnknownError';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { ITag } from 'interfaces/tags';

const StyledInput = styled(Input)(() => ({
width: '100%',
}));

interface IAddTagDialogProps {
open: boolean;
Expand All @@ -28,7 +33,7 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => {
const { classes: styles } = useStyles();
const featureId = useRequiredPathParam('featureId');
const { addTagToFeature, loading } = useFeatureApi();
const { refetch } = useTags(featureId);
const { tags, refetch } = useTags(featureId);
const [errors, setErrors] = useState({ tagError: '' });
const { setToastData } = useToast();
const [tag, setTag] = useState(DEFAULT_TAG);
Expand All @@ -39,12 +44,6 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => {
setTag(DEFAULT_TAG);
};

const setValue = (field: string, value: string) => {
const newTag = { ...tag };
newTag[field] = trim(value);
setTag(newTag);
};

const onSubmit = async (evt: React.SyntheticEvent) => {
evt.preventDefault();
if (!tag.type) {
Expand All @@ -68,6 +67,26 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => {
}
};

const isValueNotEmpty = (name: string) => name.length;
const isTagUnique = (tag: ITag) =>
!tags.some(
({ type, value }) => type === tag.type && value === tag.value
);
const isValid = isValueNotEmpty(tag.value) && isTagUnique(tag);

const onUpdateTag = (key: string, value: string) => {
setErrors({ tagError: '' });
const updatedTag = { ...tag, [key]: trim(value) };

if (!isTagUnique(updatedTag)) {
setErrors({
tagError: 'Tag already exists for this feature toggle.',
});
}

setTag(updatedTag);
};

const formId = 'add-tag-form';

return (
Expand All @@ -78,7 +97,7 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => {
primaryButtonText="Add tag"
title="Add tags to feature toggle"
onClick={onSubmit}
disabledPrimaryButton={loading}
disabledPrimaryButton={loading || !isValid}
onClose={onCancel}
formId={formId}
>
Expand All @@ -92,19 +111,20 @@ const AddTagDialog = ({ open, setOpen }: IAddTagDialogProps) => {
autoFocus
name="type"
value={tag.type}
onChange={type => setValue('type', type)}
onChange={type => onUpdateTag('type', type)}
/>
<br />
<Input
<StyledInput
label="Value"
name="value"
placeholder="Your tag"
value={tag.value}
error={Boolean(errors.tagError)}
errorText={errors.tagError}
onChange={e =>
setValue('value', e.target.value)
onUpdateTag('value', e.target.value)
}
required
/>
</section>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { FeatureOverviewSidePanelEnvironmentSwitches } from './FeatureOverviewSi
import { FeatureOverviewSidePanelTags } from './FeatureOverviewSidePanelTags/FeatureOverviewSidePanelTags';

const StyledContainer = styled('div')(({ theme }) => ({
position: 'sticky',
top: theme.spacing(2),
borderRadius: theme.shape.borderRadiusLarge,
backgroundColor: theme.palette.background.paper,
display: 'flex',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,27 @@ const StyledContainer = styled('div')(({ theme }) => ({
},
}));

const StyledLabel = styled('label')(({ theme }) => ({
const StyledLabel = styled('label')(() => ({
display: 'inline-flex',
alignItems: 'center',
cursor: 'pointer',
}));

interface IFeatureOverviewSidePanelEnvironmentSwitchProps {
env: IFeatureEnvironment;
environment: IFeatureEnvironment;
callback?: () => void;
showInfoBox: () => void;
children?: React.ReactNode;
}

export const FeatureOverviewSidePanelEnvironmentSwitch = ({
env,
environment,
callback,
showInfoBox,
children,
}: IFeatureOverviewSidePanelEnvironmentSwitchProps) => {
const { name, enabled } = environment;

const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId');
const { toggleFeatureEnvironmentOn, toggleFeatureEnvironmentOff } =
Expand All @@ -55,11 +57,11 @@ export const FeatureOverviewSidePanelEnvironmentSwitch = ({

const handleToggleEnvironmentOn = async () => {
try {
await toggleFeatureEnvironmentOn(projectId, featureId, env.name);
await toggleFeatureEnvironmentOn(projectId, featureId, name);
setToastData({
type: 'success',
title: `Available in ${env.name}`,
text: `${featureId} is now available in ${env.name} based on its defined strategies.`,
title: `Available in ${name}`,
text: `${featureId} is now available in ${name} based on its defined strategies.`,
});
refetchFeature();
if (callback) {
Expand All @@ -79,11 +81,11 @@ export const FeatureOverviewSidePanelEnvironmentSwitch = ({

const handleToggleEnvironmentOff = async () => {
try {
await toggleFeatureEnvironmentOff(projectId, featureId, env.name);
await toggleFeatureEnvironmentOff(projectId, featureId, name);
setToastData({
type: 'success',
title: `Unavailable in ${env.name}`,
text: `${featureId} is unavailable in ${env.name} and its strategies will no longer have any effect.`,
title: `Unavailable in ${name}`,
text: `${featureId} is unavailable in ${name} and its strategies will no longer have any effect.`,
});
refetchFeature();
if (callback) {
Expand All @@ -95,12 +97,12 @@ export const FeatureOverviewSidePanelEnvironmentSwitch = ({
};

const toggleEnvironment = async (e: React.ChangeEvent) => {
if (isChangeRequestConfigured(env.name)) {
if (isChangeRequestConfigured(name)) {
e.preventDefault();
onChangeRequestToggle(featureId, env.name, !env.enabled);
onChangeRequestToggle(featureId, name, !enabled);
return;
}
if (env.enabled) {
if (enabled) {
await handleToggleEnvironmentOff();
return;
}
Expand All @@ -110,21 +112,26 @@ export const FeatureOverviewSidePanelEnvironmentSwitch = ({
const defaultContent = (
<>
{' '}
<span data-loading>{env.enabled ? 'enabled' : 'disabled'} in</span>
<span data-loading>{enabled ? 'enabled' : 'disabled'} in</span>
&nbsp;
<StringTruncator text={env.name} maxWidth="120" maxLength={15} />
<StringTruncator text={name} maxWidth="120" maxLength={15} />
</>
);

return (
<StyledContainer>
<StyledLabel>
<PermissionSwitch
tooltip={
enabled
? `Disable feature in ${name}`
: `Enable feature in ${name}`
}
permission={UPDATE_FEATURE_ENVIRONMENT}
projectId={projectId}
checked={env.enabled}
checked={enabled}
onChange={toggleEnvironment}
environmentId={env.name}
environmentId={name}
/>
{children ?? defaultContent}
</StyledLabel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import EnvironmentStrategyDialog from 'component/common/EnvironmentStrategiesDia
import { IFeatureToggle } from 'interfaces/featureToggle';
import { useState } from 'react';
import { FeatureOverviewSidePanelEnvironmentSwitch } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSidePanel/FeatureOverviewSidePanelEnvironmentSwitches/FeatureOverviewSidePanelEnvironmentSwitch/FeatureOverviewSidePanelEnvironmentSwitch';
import { Link, styled } from '@mui/material';
import { Link, styled, Tooltip } from '@mui/material';
import { Link as RouterLink } from 'react-router-dom';

const StyledContainer = styled('div')(({ theme }) => ({
Expand Down Expand Up @@ -55,22 +55,24 @@ export const FeatureOverviewSidePanelEnvironmentSwitches = ({
const variantsLink = variants.length > 0 && (
<>
{' - '}
<StyledLink
component={RouterLink}
to={`/projects/${feature.project}/features/${feature.name}/variants`}
underline="hover"
>
{variants.length === 1
? '1 variant'
: `${variants.length} variants`}
</StyledLink>
<Tooltip title="View variants" arrow describeChild>
<StyledLink
component={RouterLink}
to={`/projects/${feature.project}/features/${feature.name}/variants`}
underline="hover"
>
{variants.length === 1
? '1 variant'
: `${variants.length} variants`}
</StyledLink>
</Tooltip>
</>
);

return (
<FeatureOverviewSidePanelEnvironmentSwitch
key={environment.name}
env={environment}
environment={environment}
showInfoBox={() => {
setEnvironmentName(environment.name);
setShowInfoBox(true);
Expand Down

0 comments on commit 4d5c12d

Please sign in to comment.