From 58b12250fd2823c85f5f33179e1e9888c6f25565 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Thu, 19 Jun 2025 13:57:13 +0200 Subject: [PATCH] Hack together a tab-switchable change card. --- .../ChangeRequest/ChangeRequest.tsx | 13 +- .../Changes/SwitchableChangeCard.tsx | 166 ++++++++++++++++++ 2 files changed, 174 insertions(+), 5 deletions(-) create mode 100644 frontend/src/component/changeRequest/ChangeRequest/Changes/SwitchableChangeCard.tsx diff --git a/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx b/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx index 2f460ab67b..48be164712 100644 --- a/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx @@ -1,11 +1,11 @@ import type { VFC } from 'react'; import { Box, Typography } from '@mui/material'; import type { ChangeRequestType } from '../changeRequest.types'; -import { FeatureToggleChanges } from './Changes/FeatureToggleChanges.tsx'; import { FeatureChange } from './Changes/Change/FeatureChange.tsx'; import { ChangeActions } from './Changes/Change/ChangeActions.tsx'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { SegmentChange } from './Changes/Change/SegmentChange.tsx'; +import { SwitchableChangeCard } from './Changes/SwitchableChangeCard.tsx'; interface IChangeRequestProps { changeRequest: ChangeRequestType; @@ -54,10 +54,13 @@ export const ChangeRequest: VFC = ({ } /> {changeRequest.features?.map((feature) => ( - @@ -98,7 +101,7 @@ export const ChangeRequest: VFC = ({ feature={feature} /> ) : null} - + ))} ); diff --git a/frontend/src/component/changeRequest/ChangeRequest/Changes/SwitchableChangeCard.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/SwitchableChangeCard.tsx new file mode 100644 index 0000000000..8d48ca2a3c --- /dev/null +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/SwitchableChangeCard.tsx @@ -0,0 +1,166 @@ +import type React from 'react'; +import { type PropsWithChildren, useState, type FC, useId } from 'react'; +import { Link as RouterLink } from 'react-router-dom'; +import { Box, Card, Typography, Link, styled, Tabs, Tab } from '@mui/material'; +import { ConflictWarning } from './Change/ConflictWarning.tsx'; + +type SegmentProps = { + resourceType: 'Segment'; + segment: { + id: number; + name: string; + }; +}; + +type FlagProps = { + resourceType: 'Feature flag'; + flag: { + projectId: string; + name: string; + }; +}; + +type Props = { + conflict?: string; + onNavigate?: () => void; + children?: React.ReactNode; + diff?: React.ReactNode; +} & (SegmentProps | FlagProps); + +const HeaderGroup = styled('hgroup', { + shouldForwardProp: (prop) => prop !== 'conflict', +})<{ conflict?: string }>(({ theme, conflict }) => ({ + display: 'flex', + paddingTop: theme.spacing(conflict ? 0 : 2), + paddingBottom: theme.spacing(2), + paddingInline: theme.spacing(3), +})); + +const BottomRow = styled('div')(({ theme }) => ({ + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', +})); + +const TabPanel: FC< + PropsWithChildren<{ + index: number; + value: number; + id: string; + 'aria-labelledby': string; + }> +> = ({ children, index, value, id, 'aria-labelledby': ariaLabelledBy }) => { + return ( + + ); +}; + +const tabA11yProps = (baseId: string) => (index: number) => ({ + id: `${baseId}-tab-${index}`, + 'aria-controls': `${baseId}-${index}`, +}); + +export const SwitchableChangeCard: FC = ({ + conflict, + onNavigate, + children, + diff, + resourceType, + ...resourceProps +}) => { + const [tabIndex, setTabIndex] = useState(0); + const baseId = useId(); + const allyProps = tabA11yProps(baseId); + + const [url, name] = + 'segment' in resourceProps + ? [ + `/segments/edit/${resourceProps.segment.id}`, + resourceProps.segment.name, + ] + : [ + `/projects/${resourceProps.flag.projectId}/features/${resourceProps.flag.name}`, + resourceProps.flag.name, + ]; + + return ( + ({ + marginTop: theme.spacing(2), + overflow: 'hidden', + })} + > + ({ + backgroundColor: theme.palette.neutral.light, + borderRadius: (theme) => + `${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px 0 0`, + border: '1px solid', + borderColor: (theme) => + conflict + ? theme.palette.warning.border + : theme.palette.divider, + borderBottom: 'none', + overflow: 'hidden', + })} + > + + + + {resourceType}: + + + {name} + + + + setTabIndex(newValue)} + > + + + + + + + {children} + + + {diff} + + + ); +};