mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-23 00:22:19 +01:00
feat: Playground environment diff table (#4002)
This commit is contained in:
parent
eb8f16da8d
commit
16a3f6069c
@ -24,7 +24,7 @@ import { AdvancedPlaygroundResponseSchema } from 'openapi';
|
||||
export const AdvancedPlayground: VFC<{}> = () => {
|
||||
const { environments: availableEnvironments } = useEnvironments();
|
||||
const theme = useTheme();
|
||||
const matches = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
const matches = true;
|
||||
|
||||
const [environments, setEnvironments] = useState<string[]>([]);
|
||||
const [projects, setProjects] = useState<string[]>([]);
|
||||
|
@ -0,0 +1,76 @@
|
||||
import { Link, Popover, styled, Typography, useTheme } from '@mui/material';
|
||||
import { flexRow } from '../../../../../themes/themeStyles';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
AdvancedPlaygroundEnvironmentFeatureSchema,
|
||||
AdvancedPlaygroundFeatureSchemaEnvironments,
|
||||
} from 'openapi';
|
||||
import { PlaygroundEnvironmentTable } from '../../PlaygroundEnvironmentTable/PlaygroundEnvironmentTable';
|
||||
import { PlaygroundEnvironmentDiffTable } from '../../PlaygroundEnvironmentTable/PlaygroundEnvironmentDiffTable';
|
||||
|
||||
const StyledContainer = styled(
|
||||
'div',
|
||||
{}
|
||||
)(({ theme }) => ({
|
||||
flexGrow: 0,
|
||||
...flexRow,
|
||||
justifyContent: 'flex-start',
|
||||
margin: theme.spacing(0, 1.5),
|
||||
}));
|
||||
|
||||
const StyledButton = styled(Link)(({ theme }) => ({
|
||||
textAlign: 'left',
|
||||
textDecorationStyle: 'dotted',
|
||||
textUnderlineOffset: theme.spacing(0.75),
|
||||
color: theme.palette.neutral.dark,
|
||||
}));
|
||||
|
||||
export interface IAdvancedPlaygroundEnvironmentCellProps {
|
||||
value: AdvancedPlaygroundFeatureSchemaEnvironments;
|
||||
}
|
||||
|
||||
export const AdvancedPlaygroundEnvironmentDiffCell = ({
|
||||
value,
|
||||
}: IAdvancedPlaygroundEnvironmentCellProps) => {
|
||||
const theme = useTheme();
|
||||
const [anchor, setAnchorEl] = useState<null | Element>(null);
|
||||
|
||||
const onOpen = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) =>
|
||||
setAnchorEl(event.currentTarget);
|
||||
|
||||
const onClose = () => setAnchorEl(null);
|
||||
|
||||
const open = Boolean(anchor);
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<>
|
||||
<StyledButton variant={'body2'} onClick={onOpen}>
|
||||
Preview diff
|
||||
</StyledButton>
|
||||
|
||||
<Popover
|
||||
open={open}
|
||||
id={`${value}-result-details`}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
borderRadius: `${theme.shape.borderRadiusLarge}px`,
|
||||
padding: theme.spacing(3),
|
||||
},
|
||||
}}
|
||||
onClose={onClose}
|
||||
anchorEl={anchor}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: -320,
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle2" sx={{ mb: 3 }}>
|
||||
Environments diff
|
||||
</Typography>
|
||||
<PlaygroundEnvironmentDiffTable features={value} />
|
||||
</Popover>
|
||||
</>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
@ -34,6 +34,7 @@ import {
|
||||
AdvancedPlaygroundFeatureSchema,
|
||||
} from 'openapi';
|
||||
import { capitalizeFirst } from 'utils/capitalizeFirst';
|
||||
import { AdvancedPlaygroundEnvironmentDiffCell } from './AdvancedPlaygroundEnvironmentCell/AdvancedPlaygroundEnvironmentDiffCell';
|
||||
|
||||
const defaultSort: SortingRule<string> = { id: 'name' };
|
||||
const { value, setValue } = createLocalStorage(
|
||||
@ -111,7 +112,9 @@ export const AdvancedPlaygroundResultsTable = ({
|
||||
id: 'diff',
|
||||
align: 'left',
|
||||
Cell: ({ row }: any) => (
|
||||
<StyledButton variant={'body2'}>Preview diff</StyledButton>
|
||||
<AdvancedPlaygroundEnvironmentDiffCell
|
||||
value={row.original.environments}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
@ -0,0 +1,101 @@
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import {
|
||||
useFlexLayout,
|
||||
useGlobalFilter,
|
||||
useSortBy,
|
||||
useTable,
|
||||
} from 'react-table';
|
||||
|
||||
import { VirtualizedTable } from 'component/common/Table';
|
||||
import { sortTypes } from 'utils/sortTypes';
|
||||
import { AdvancedPlaygroundFeatureSchemaEnvironments } from 'openapi';
|
||||
import { Box } from '@mui/material';
|
||||
import { FeatureStatusCell } from '../PlaygroundResultsTable/FeatureStatusCell/FeatureStatusCell';
|
||||
import { HighlightCell } from '../../../common/Table/cells/HighlightCell/HighlightCell';
|
||||
import { capitalizeFirst } from 'utils/capitalizeFirst';
|
||||
|
||||
interface IPlaygroundEnvironmentTableProps {
|
||||
features: AdvancedPlaygroundFeatureSchemaEnvironments;
|
||||
}
|
||||
|
||||
export const PlaygroundEnvironmentDiffTable = ({
|
||||
features,
|
||||
}: IPlaygroundEnvironmentTableProps) => {
|
||||
const environments = Object.keys(features);
|
||||
const firstEnvFeatures = features[environments[0]];
|
||||
const firstContext = firstEnvFeatures[0].context;
|
||||
|
||||
const data = useMemo(
|
||||
() =>
|
||||
firstEnvFeatures.map((item, index) => ({
|
||||
...Object.fromEntries(
|
||||
environments.map(env => [env, features[env][index]])
|
||||
),
|
||||
})),
|
||||
[JSON.stringify(features)]
|
||||
);
|
||||
type RowType = typeof data[0];
|
||||
|
||||
const contextFieldsHeaders = Object.keys(firstContext).map(
|
||||
contextField => ({
|
||||
Header: capitalizeFirst(contextField),
|
||||
accessor: `${environments[0]}.context.${contextField}`,
|
||||
minWidth: 160,
|
||||
Cell: HighlightCell,
|
||||
})
|
||||
);
|
||||
|
||||
const environmentHeaders = environments.map(environment => ({
|
||||
Header: environment,
|
||||
accessor: (row: RowType) =>
|
||||
row[environment]?.isEnabled
|
||||
? 'true'
|
||||
: row[environment]?.strategies?.result === 'unknown'
|
||||
? 'unknown'
|
||||
: 'false',
|
||||
Cell: ({ row }: { row: { original: RowType } }) => {
|
||||
return <FeatureStatusCell feature={row.original[environment]} />;
|
||||
},
|
||||
sortType: 'playgroundResultState',
|
||||
maxWidth: 120,
|
||||
}));
|
||||
|
||||
const COLUMNS = useMemo(() => {
|
||||
return [...contextFieldsHeaders, ...environmentHeaders];
|
||||
}, []);
|
||||
|
||||
const { headerGroups, rows, prepareRow } = useTable(
|
||||
{
|
||||
columns: COLUMNS as any[],
|
||||
data,
|
||||
sortTypes,
|
||||
autoResetGlobalFilter: false,
|
||||
autoResetHiddenColumns: false,
|
||||
autoResetSortBy: false,
|
||||
disableSortRemove: true,
|
||||
disableMultiSort: true,
|
||||
},
|
||||
useGlobalFilter,
|
||||
useFlexLayout,
|
||||
useSortBy
|
||||
);
|
||||
|
||||
const parentRef = useRef<HTMLElement | null>(null);
|
||||
|
||||
return (
|
||||
<Box
|
||||
ref={parentRef}
|
||||
sx={{
|
||||
overflow: 'auto',
|
||||
maxHeight: '800px',
|
||||
}}
|
||||
>
|
||||
<VirtualizedTable
|
||||
parentRef={parentRef}
|
||||
rows={rows}
|
||||
headerGroups={headerGroups}
|
||||
prepareRow={prepareRow}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -42,5 +42,5 @@ test('should render environment table', async () => {
|
||||
expect(screen.getByText('clientA')).toBeInTheDocument();
|
||||
expect(screen.getByText('variantName')).toBeInTheDocument();
|
||||
expect(screen.getByText('False')).toBeInTheDocument();
|
||||
expect(screen.queryByText('myapp')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('myapp')).toBeInTheDocument();
|
||||
});
|
||||
|
@ -30,14 +30,14 @@ export const PlaygroundEnvironmentTable = ({
|
||||
const theme = useTheme();
|
||||
const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
const dynamicHeaders = Object.keys(features[0].context)
|
||||
.filter(contextField => contextField !== 'appName')
|
||||
.map(contextField => ({
|
||||
const dynamicHeaders = Object.keys(features[0].context).map(
|
||||
contextField => ({
|
||||
Header: capitalizeFirst(contextField),
|
||||
accessor: `context.${contextField}`,
|
||||
minWidth: 160,
|
||||
Cell: HighlightCell,
|
||||
}));
|
||||
})
|
||||
);
|
||||
|
||||
const COLUMNS = useMemo(() => {
|
||||
return [
|
||||
@ -108,6 +108,11 @@ export const PlaygroundEnvironmentTable = ({
|
||||
columns: COLUMNS as any,
|
||||
data: features,
|
||||
sortTypes,
|
||||
autoResetGlobalFilter: false,
|
||||
autoResetHiddenColumns: false,
|
||||
autoResetSortBy: false,
|
||||
disableSortRemove: true,
|
||||
disableMultiSort: true,
|
||||
},
|
||||
useGlobalFilter,
|
||||
useFlexLayout,
|
||||
|
Loading…
Reference in New Issue
Block a user