mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	feat: add creation buttons to application features and strategies (#6387)

This commit is contained in:
		
							parent
							
								
									68095916e8
								
							
						
					
					
						commit
						a958797a8a
					
				@ -1,12 +1,20 @@
 | 
				
			|||||||
import { Box, styled } from '@mui/material';
 | 
					import { Box, styled } from '@mui/material';
 | 
				
			||||||
import { ConditionallyRender } from '../../common/ConditionallyRender/ConditionallyRender';
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
import { WarningAmberRounded } from '@mui/icons-material';
 | 
					import { WarningAmberRounded } from '@mui/icons-material';
 | 
				
			||||||
import { ApplicationOverviewIssuesSchema } from 'openapi';
 | 
					import { ApplicationOverviewIssuesSchema } from 'openapi';
 | 
				
			||||||
 | 
					import { Link } from 'react-router-dom';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    CREATE_FEATURE,
 | 
				
			||||||
 | 
					    CREATE_STRATEGY,
 | 
				
			||||||
 | 
					} from 'component/providers/AccessProvider/permissions';
 | 
				
			||||||
 | 
					import { useContext } from 'react';
 | 
				
			||||||
 | 
					import AccessContext from 'contexts/AccessContext';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const WarningContainer = styled(Box)(({ theme }) => ({
 | 
					const WarningContainer = styled(Box)(({ theme }) => ({
 | 
				
			||||||
    display: 'flex',
 | 
					    display: 'flex',
 | 
				
			||||||
    flexDirection: 'column',
 | 
					    flexDirection: 'column',
 | 
				
			||||||
    alignSelf: 'stretch',
 | 
					    alignSelf: 'stretch',
 | 
				
			||||||
 | 
					    fontSize: theme.fontSizes.smallBody,
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const WarningHeader = styled(Box)(({ theme }) => ({
 | 
					const WarningHeader = styled(Box)(({ theme }) => ({
 | 
				
			||||||
@ -21,11 +29,7 @@ const WarningHeader = styled(Box)(({ theme }) => ({
 | 
				
			|||||||
    color: theme.palette.warning.main,
 | 
					    color: theme.palette.warning.main,
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const SmallText = styled(Box)(({ theme }) => ({
 | 
					const WarningHeaderText = styled(Box)(({ theme }) => ({
 | 
				
			||||||
    fontSize: theme.fontSizes.smallBody,
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const WarningHeaderText = styled(SmallText)(({ theme }) => ({
 | 
					 | 
				
			||||||
    color: theme.palette.warning.dark,
 | 
					    color: theme.palette.warning.dark,
 | 
				
			||||||
    fontWeight: theme.fontWeight.bold,
 | 
					    fontWeight: theme.fontWeight.bold,
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
@ -36,7 +40,6 @@ const StyledList = styled('ul')(({ theme }) => ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const StyledListElement = styled('li')(({ theme }) => ({
 | 
					const StyledListElement = styled('li')(({ theme }) => ({
 | 
				
			||||||
    fontWeight: theme.fontWeight.bold,
 | 
					    fontWeight: theme.fontWeight.bold,
 | 
				
			||||||
    fontSize: theme.fontSizes.smallBody,
 | 
					 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const IssueContainer = styled(Box)(({ theme }) => ({
 | 
					const IssueContainer = styled(Box)(({ theme }) => ({
 | 
				
			||||||
@ -62,6 +65,20 @@ const IssueTextContainer = styled(Box)(({ theme }) => ({
 | 
				
			|||||||
    border: `1px solid ${theme.palette.divider}`,
 | 
					    border: `1px solid ${theme.palette.divider}`,
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const IssueRowContainer = styled(Box)(({ theme }) => ({
 | 
				
			||||||
 | 
					    display: 'flex',
 | 
				
			||||||
 | 
					    flexDirection: 'row',
 | 
				
			||||||
 | 
					    gap: theme.spacing(1.5),
 | 
				
			||||||
 | 
					    alignItems: 'center',
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledLink = styled(Link)(() => ({
 | 
				
			||||||
 | 
					    overflow: 'hidden',
 | 
				
			||||||
 | 
					    textOverflow: 'ellipsis',
 | 
				
			||||||
 | 
					    whiteSpace: 'nowrap',
 | 
				
			||||||
 | 
					    textDecoration: 'underline',
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface IApplicationIssuesProps {
 | 
					export interface IApplicationIssuesProps {
 | 
				
			||||||
    issues: ApplicationOverviewIssuesSchema[];
 | 
					    issues: ApplicationOverviewIssuesSchema[];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -85,6 +102,7 @@ const resolveIssueText = (issue: ApplicationOverviewIssuesSchema) => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ApplicationIssues = ({ issues }: IApplicationIssuesProps) => {
 | 
					export const ApplicationIssues = ({ issues }: IApplicationIssuesProps) => {
 | 
				
			||||||
 | 
					    const { hasAccess } = useContext(AccessContext);
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <ConditionallyRender
 | 
					        <ConditionallyRender
 | 
				
			||||||
            condition={issues.length > 0}
 | 
					            condition={issues.length > 0}
 | 
				
			||||||
@ -100,12 +118,44 @@ export const ApplicationIssues = ({ issues }: IApplicationIssuesProps) => {
 | 
				
			|||||||
                    <IssueContainer>
 | 
					                    <IssueContainer>
 | 
				
			||||||
                        {issues.map((issue) => (
 | 
					                        {issues.map((issue) => (
 | 
				
			||||||
                            <IssueTextContainer key={issue.type}>
 | 
					                            <IssueTextContainer key={issue.type}>
 | 
				
			||||||
                                <SmallText>{resolveIssueText(issue)}</SmallText>
 | 
					                                {resolveIssueText(issue)}
 | 
				
			||||||
                                <StyledList>
 | 
					                                <StyledList>
 | 
				
			||||||
                                    {issue.items.map((item) => (
 | 
					                                    {issue.items.map((item) => (
 | 
				
			||||||
                                        <StyledListElement key={item}>
 | 
					                                        <IssueRowContainer key={item}>
 | 
				
			||||||
 | 
					                                            <StyledListElement>
 | 
				
			||||||
                                                {item}
 | 
					                                                {item}
 | 
				
			||||||
                                            </StyledListElement>
 | 
					                                            </StyledListElement>
 | 
				
			||||||
 | 
					                                            <ConditionallyRender
 | 
				
			||||||
 | 
					                                                condition={
 | 
				
			||||||
 | 
					                                                    issue.type ===
 | 
				
			||||||
 | 
					                                                        'missingFeatures' &&
 | 
				
			||||||
 | 
					                                                    hasAccess(CREATE_FEATURE)
 | 
				
			||||||
 | 
					                                                }
 | 
				
			||||||
 | 
					                                                show={
 | 
				
			||||||
 | 
					                                                    <StyledLink
 | 
				
			||||||
 | 
					                                                        key={item}
 | 
				
			||||||
 | 
					                                                        to={`/projects/default/create-toggle?name=${item}`}
 | 
				
			||||||
 | 
					                                                    >
 | 
				
			||||||
 | 
					                                                        Create feature flag
 | 
				
			||||||
 | 
					                                                    </StyledLink>
 | 
				
			||||||
 | 
					                                                }
 | 
				
			||||||
 | 
					                                            />
 | 
				
			||||||
 | 
					                                            <ConditionallyRender
 | 
				
			||||||
 | 
					                                                condition={
 | 
				
			||||||
 | 
					                                                    issue.type ===
 | 
				
			||||||
 | 
					                                                        'missingStrategies' &&
 | 
				
			||||||
 | 
					                                                    hasAccess(CREATE_STRATEGY)
 | 
				
			||||||
 | 
					                                                }
 | 
				
			||||||
 | 
					                                                show={
 | 
				
			||||||
 | 
					                                                    <StyledLink
 | 
				
			||||||
 | 
					                                                        key={item}
 | 
				
			||||||
 | 
					                                                        to={`/strategies/create`}
 | 
				
			||||||
 | 
					                                                    >
 | 
				
			||||||
 | 
					                                                        Create strategy type
 | 
				
			||||||
 | 
					                                                    </StyledLink>
 | 
				
			||||||
 | 
					                                                }
 | 
				
			||||||
 | 
					                                            />
 | 
				
			||||||
 | 
					                                        </IssueRowContainer>
 | 
				
			||||||
                                    ))}
 | 
					                                    ))}
 | 
				
			||||||
                                </StyledList>
 | 
					                                </StyledList>
 | 
				
			||||||
                            </IssueTextContainer>
 | 
					                            </IssueTextContainer>
 | 
				
			||||||
 | 
				
			|||||||
@ -72,3 +72,48 @@ test('Display application overview without environments', async () => {
 | 
				
			|||||||
    await screen.findByText('my-app');
 | 
					    await screen.findByText('my-app');
 | 
				
			||||||
    await screen.findByText('No data available.');
 | 
					    await screen.findByText('No data available.');
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('Display application with issues', async () => {
 | 
				
			||||||
 | 
					    setupApi({
 | 
				
			||||||
 | 
					        environments: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                name: 'development',
 | 
				
			||||||
 | 
					                instanceCount: 999,
 | 
				
			||||||
 | 
					                lastSeen: new Date().toISOString(),
 | 
				
			||||||
 | 
					                sdks: ['unleash-client-node:5.5.0-beta.0'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        issues: [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                type: 'missingFeatures',
 | 
				
			||||||
 | 
					                items: ['feature1'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                type: 'missingStrategies',
 | 
				
			||||||
 | 
					                items: ['strategy1'],
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					        featureCount: 1,
 | 
				
			||||||
 | 
					        projects: ['default'],
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					    render(
 | 
				
			||||||
 | 
					        <Routes>
 | 
				
			||||||
 | 
					            <Route
 | 
				
			||||||
 | 
					                path={'/applications/:name'}
 | 
				
			||||||
 | 
					                element={<ApplicationOverview />}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					        </Routes>,
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            route: '/applications/my-app',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await screen.findByText(
 | 
				
			||||||
 | 
					        'We detected 1 feature flag defined in the SDK that does not exist in Unleash',
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    await screen.findByText(
 | 
				
			||||||
 | 
					        'We detected 1 strategy type defined in the SDK that does not exist in Unleash',
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    await screen.findByText('feature1');
 | 
				
			||||||
 | 
					    await screen.findByText('strategy1');
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user