1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-29 01:15:48 +02:00

Merge branch 'main' into task/Add_strategy_information_to_playground_results

This commit is contained in:
Tymoteusz Czech 2022-08-05 10:23:08 +02:00 committed by GitHub
commit b063cfa180
7 changed files with 135 additions and 51 deletions

View File

@ -63,7 +63,7 @@
"@types/react-timeago": "4.1.3",
"@types/semver": "7.3.10",
"@vitejs/plugin-react": "1.3.2",
"chart.js": "3.8.2",
"chart.js": "3.9.1",
"chartjs-adapter-date-fns": "2.0.0",
"classnames": "2.3.1",
"copy-to-clipboard": "3.3.2",

View File

@ -65,7 +65,7 @@ export const SegmentItem: VFC<ISegmentItemProps> = ({
condition={segment!.constraints?.length > 0}
show={
<ConstraintAccordionList
constraints={segment!.constraints}
constraints={segment.constraints}
showLabel={false}
/>
}

View File

@ -50,9 +50,7 @@ export const FeatureStrategySegmentList = ({
</div>
<ConditionallyRender
condition={Boolean(preview)}
show={() => (
<SegmentItem segment={preview as ISegment} isExpanded />
)}
show={() => <SegmentItem segment={preview!} isExpanded />}
/>
</>
);

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { DragEventHandler, RefObject, useEffect, useState } from 'react';
import { Alert } from '@mui/material';
import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
import { formatUnknownError } from 'utils/formatUnknownError';
@ -27,10 +27,14 @@ const EnvironmentAccordionBody = ({
const { setStrategiesSortOrder } = useFeatureStrategyApi();
const { setToastData, setToastApiError } = useToast();
const { refetchFeature } = useFeature(projectId, featureId);
const [strategies, setStrategies] = useState(
featureEnvironment?.strategies || []
);
const [dragItem, setDragItem] = useState<{
id: string;
index: number;
height: number;
} | null>(null);
const { classes: styles } = useStyles();
useEffect(() => {
// Use state to enable drag and drop, but switch to API output when it arrives
@ -41,25 +45,13 @@ const EnvironmentAccordionBody = ({
return null;
}
const onDragAndDrop = async (
from: number,
to: number,
dropped?: boolean
) => {
if (from !== to && dropped) {
const newStrategies = [...strategies];
const movedStrategy = newStrategies.splice(from, 1)[0];
newStrategies.splice(to, 0, movedStrategy);
setStrategies(newStrategies);
const onReorder = async (payload: { id: string; sortOrder: number }[]) => {
try {
await setStrategiesSortOrder(
projectId,
featureId,
featureEnvironment.name,
[...newStrategies].map((strategy, sortOrder) => ({
id: strategy.id,
sortOrder,
}))
payload
);
refetchFeature();
setToastData({
@ -69,9 +61,72 @@ const EnvironmentAccordionBody = ({
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
const onDragStartRef =
(
ref: RefObject<HTMLDivElement>,
index: number
): DragEventHandler<HTMLButtonElement> =>
event => {
setDragItem({
id: strategies[index].id,
index,
height: ref.current?.offsetHeight || 0,
});
if (ref?.current) {
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/html', ref.current.outerHTML);
event.dataTransfer.setDragImage(ref.current, 20, 20);
}
};
const onDragOver =
(targetId: string) =>
(
ref: RefObject<HTMLDivElement>,
targetIndex: number
): DragEventHandler<HTMLDivElement> =>
event => {
if (dragItem === null || ref.current === null) return;
if (dragItem.index === targetIndex || targetId === dragItem.id)
return;
const { top, bottom } = ref.current.getBoundingClientRect();
const overTargetTop = event.clientY - top < dragItem.height;
const overTargetBottom = bottom - event.clientY < dragItem.height;
const draggingUp = dragItem.index > targetIndex;
// prevent oscillating by only reordering if there is sufficient space
if (
(overTargetTop && draggingUp) ||
(overTargetBottom && !draggingUp)
) {
const newStrategies = [...strategies];
const movedStrategy = newStrategies.splice(
dragItem.index,
1
)[0];
newStrategies.splice(targetIndex, 0, movedStrategy);
setStrategies(newStrategies);
setDragItem({
...dragItem,
index: targetIndex,
});
}
};
const onDragEnd = () => {
setDragItem(null);
onReorder(
strategies.map((strategy, sortOrder) => ({
id: strategy.id,
sortOrder,
}))
);
};
return (
<div className={styles.accordionBody}>
<div className={styles.accordionBodyInnerContainer}>
@ -91,11 +146,14 @@ const EnvironmentAccordionBody = ({
{strategies.map((strategy, index) => (
<StrategyDraggableItem
key={strategy.id}
onDragAndDrop={onDragAndDrop}
strategy={strategy}
index={index}
environmentName={featureEnvironment.name}
otherEnvironments={otherEnvironments}
isDragging={dragItem?.id === strategy.id}
onDragStartRef={onDragStartRef}
onDragOver={onDragOver(strategy.id)}
onDragEnd={onDragEnd}
/>
))}
</>

View File

@ -1,9 +1,9 @@
import { Box, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
import { MoveListItem, useDragItem } from 'hooks/useDragItem';
import { IFeatureEnvironment } from 'interfaces/featureToggle';
import { IFeatureStrategy } from 'interfaces/strategy';
import { DragEventHandler, RefObject, useRef } from 'react';
import { StrategyItem } from './StrategyItem/StrategyItem';
interface IStrategyDraggableItemProps {
@ -11,7 +11,16 @@ interface IStrategyDraggableItemProps {
environmentName: string;
index: number;
otherEnvironments?: IFeatureEnvironment['name'][];
onDragAndDrop: MoveListItem;
isDragging?: boolean;
onDragStartRef: (
ref: RefObject<HTMLDivElement>,
index: number
) => DragEventHandler<HTMLButtonElement>;
onDragOver: (
ref: RefObject<HTMLDivElement>,
index: number
) => DragEventHandler<HTMLDivElement>;
onDragEnd: () => void;
}
const StyledIndexLabel = styled('div')(({ theme }) => ({
@ -31,12 +40,20 @@ export const StrategyDraggableItem = ({
index,
environmentName,
otherEnvironments,
onDragAndDrop,
isDragging,
onDragStartRef,
onDragOver,
onDragEnd,
}: IStrategyDraggableItemProps) => {
const ref = useDragItem(index, onDragAndDrop);
const ref = useRef<HTMLDivElement>(null);
return (
<Box key={strategy.id} ref={ref}>
<Box
key={strategy.id}
ref={ref}
onDragOver={onDragOver(ref, index)}
sx={{ opacity: isDragging ? '0.5' : '1' }}
>
<ConditionallyRender
condition={index > 0}
show={<StrategySeparator text="OR" />}
@ -47,7 +64,8 @@ export const StrategyDraggableItem = ({
strategy={strategy}
environmentId={environmentName}
otherEnvironments={otherEnvironments}
isDraggable
onDragStart={onDragStartRef(ref, index)}
onDragEnd={onDragEnd}
/>
</Box>
</Box>

View File

@ -1,3 +1,4 @@
import { DragEventHandler } from 'react';
import { DragIndicator, Edit } from '@mui/icons-material';
import { styled, useTheme, IconButton } from '@mui/material';
import { Link } from 'react-router-dom';
@ -22,7 +23,8 @@ import { useStyles } from './StrategyItem.styles';
interface IStrategyItemProps {
environmentId: string;
strategy: IFeatureStrategy;
isDraggable?: boolean;
onDragStart?: DragEventHandler<HTMLButtonElement>;
onDragEnd?: DragEventHandler<HTMLButtonElement>;
otherEnvironments?: IFeatureEnvironment['name'][];
}
@ -35,7 +37,8 @@ const DragIcon = styled(IconButton)(({ theme }) => ({
export const StrategyItem = ({
environmentId,
strategy,
isDraggable,
onDragStart,
onDragEnd,
otherEnvironments,
}: IStrategyItemProps) => {
const projectId = useRequiredPathParam('projectId');
@ -55,13 +58,20 @@ export const StrategyItem = ({
<div className={styles.container}>
<div
className={classNames(styles.header, {
[styles.headerDraggable]: isDraggable,
[styles.headerDraggable]: Boolean(onDragStart),
})}
>
<ConditionallyRender
condition={Boolean(isDraggable)}
condition={Boolean(onDragStart)}
show={() => (
<DragIcon disableRipple disabled size="small">
<DragIcon
draggable
disableRipple
size="small"
onDragStart={onDragStart}
onDragEnd={onDragEnd}
sx={{ cursor: 'move' }}
>
<DragIndicator
titleAccess="Drag to reorder"
cursor="grab"

View File

@ -2919,10 +2919,10 @@ chardet@^0.7.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
chart.js@3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.8.2.tgz#e3ebb88f7072780eec4183a788a990f4a58ba7a1"
integrity sha512-7rqSlHWMUKFyBDOJvmFGW2lxULtcwaPLegDjX/Nu5j6QybY+GCiQkEY+6cqHw62S5tcwXMD8Y+H5OBGoR7d+ZQ==
chart.js@3.9.1:
version "3.9.1"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.9.1.tgz#3abf2c775169c4c71217a107163ac708515924b8"
integrity sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==
chartjs-adapter-date-fns@2.0.0:
version "2.0.0"