1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-10-27 11:02:16 +01:00

chore: remove legacy flag UI (#10781)

This commit is contained in:
Tymoteusz Czech 2025-10-14 11:00:51 +02:00 committed by GitHub
parent 7efd707dee
commit b5d1f6e075
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 57 additions and 333 deletions

View File

@ -262,7 +262,6 @@ test('clears lifecycle filter when switching to archived view', async () => {
testServerRoute(server, '/api/admin/ui-config', {
flags: {
flagCreator: true,
flagsUiFilterRefactor: true,
},
});

View File

@ -1,4 +1,3 @@
import { ReactComponent as ImportSvg } from 'assets/icons/import.svg';
import { useCallback, useMemo, useState } from 'react';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { PageContent } from 'component/common/PageContent/PageContent';
@ -20,7 +19,6 @@ import {
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { useFeatureToggleSwitch } from '../ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch.tsx';
import useLoading from 'hooks/useLoading';
import { ProjectFeatureTogglesHeader as LegacyProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/LegacyProjectFeatureTogglesHeader.tsx';
import { createColumnHelper, useReactTable } from '@tanstack/react-table';
import { withTableState } from 'utils/withTableState';
import type { FeatureSearchResponseSchema } from 'openapi';
@ -50,14 +48,10 @@ import { ProjectOnboarded } from 'component/onboarding/flow/ProjectOnboarded';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import { ArchivedFeatureActionCell } from '../../../archive/ArchiveTable/ArchivedFeatureActionCell/ArchivedFeatureActionCell.tsx';
import { ArchiveBatchActions } from '../../../archive/ArchiveTable/ArchiveBatchActions.tsx';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { UPDATE_FEATURE } from '@server/types/permissions';
import { ImportModal } from '../Import/ImportModal.tsx';
import { IMPORT_BUTTON } from 'utils/testIds';
import { ProjectCleanupReminder } from './ProjectCleanupReminder/ProjectCleanupReminder.tsx';
import { formatEnvironmentColumnId } from './formatEnvironmentColumnId.ts';
import { ProjectFeaturesColumnsMenu } from './ProjectFeaturesColumnsMenu/ProjectFeaturesColumnsMenu.tsx';
import { useUiFlag } from 'hooks/useUiFlag.ts';
import { ProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/ProjectFeatureTogglesHeader.tsx';
import { ProjectFlagsSearch } from './ProjectFlagsSearch/ProjectFlagsSearch.tsx';
@ -74,12 +68,6 @@ const Container = styled('div')(({ theme }) => ({
gap: theme.spacing(2),
}));
const LegacyFilterRow = styled('div')(({ theme }) => ({
display: 'flex',
flexFlow: 'row wrap',
justifyContent: 'space-between',
}));
const FiltersContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
@ -95,12 +83,6 @@ const FilterRow = styled('div')({
alignItems: 'center',
});
const ButtonGroup = styled('div')(({ theme }) => ({
display: 'flex',
gap: theme.spacing(1),
paddingInline: theme.spacing(1.5),
}));
const LinkToggle = styled('button')(({ theme }) => ({
background: 'none',
border: 'none',
@ -126,7 +108,6 @@ export const ProjectFeatureToggles = ({
const { project } = useProjectOverview(projectId);
const [connectSdkOpen, setConnectSdkOpen] = useState(false);
const [modalOpen, setModalOpen] = useState(false);
const flagsUiFilterRefactorEnabled = useUiFlag('flagsUiFilterRefactor');
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
@ -551,48 +532,23 @@ export const ProjectFeatureToggles = ({
<PageContent
disableLoading
header={
flagsUiFilterRefactorEnabled ? (
<ProjectFeatureTogglesHeader
isLoading={initialLoad}
totalItems={total}
environmentsToExport={environments}
actions={
flagsUiFilterRefactorEnabled ? (
<LinkToggle
type='button'
onClick={toggleArchived}
>
{showArchived
? 'View active flags'
: 'View archived flags'}
</LinkToggle>
) : null
}
title={
showArchived
? 'Archived feature flags'
: 'Feature flags'
}
/>
) : (
<LegacyProjectFeatureTogglesHeader
isLoading={initialLoad}
totalItems={total}
searchQuery={tableState.query || ''}
onChangeSearchQuery={(query) => {
setTableState({ query });
}}
dataToExport={data}
environmentsToExport={environments}
actions={
<ProjectFeaturesColumnsMenu
columnVisibility={columnVisibility}
environments={environments}
onToggle={onToggleColumnVisibility}
/>
}
/>
)
<ProjectFeatureTogglesHeader
isLoading={initialLoad}
totalItems={total}
environmentsToExport={environments}
actions={
<LinkToggle type='button' onClick={toggleArchived}>
{showArchived
? 'View active flags'
: 'View archived flags'}
</LinkToggle>
}
title={
showArchived
? 'Archived feature flags'
: 'Feature flags'
}
/>
}
bodyClass='noop'
style={{ cursor: 'inherit' }}
@ -602,50 +558,25 @@ export const ProjectFeatureToggles = ({
aria-busy={isPlaceholder}
aria-live='polite'
>
{flagsUiFilterRefactorEnabled ? (
<FiltersContainer>
<FilterRow>
{showArchived ? (
<Box sx={{ marginRight: 'auto' }}>
<ProjectOverviewFilters
project={projectId}
onChange={setTableState}
state={filterState}
/>
</Box>
) : (
<ProjectLifecycleFilters
projectId={projectId}
state={filterState}
onChange={setTableState}
total={loading ? undefined : total}
/>
)}
{isSmallScreen ? null : (
<ProjectFlagsSearch
searchQuery={tableState.query || ''}
onChangeSearchQuery={(query) => {
setTableState({ query });
}}
isLoading={loading}
/>
)}
<ProjectFeaturesColumnsMenu
columnVisibility={columnVisibility}
environments={environments}
onToggle={onToggleColumnVisibility}
/>
</FilterRow>
{showArchived ? null : (
<FilterRow>
<FiltersContainer>
<FilterRow>
{showArchived ? (
<Box sx={{ marginRight: 'auto' }}>
<ProjectOverviewFilters
project={projectId}
onChange={setTableState}
state={filterState}
/>
</FilterRow>
</Box>
) : (
<ProjectLifecycleFilters
projectId={projectId}
state={filterState}
onChange={setTableState}
total={loading ? undefined : total}
/>
)}
{isSmallScreen ? (
{isSmallScreen ? null : (
<ProjectFlagsSearch
searchQuery={tableState.query || ''}
onChangeSearchQuery={(query) => {
@ -653,35 +584,32 @@ export const ProjectFeatureToggles = ({
}}
isLoading={loading}
/>
) : null}
</FiltersContainer>
) : (
<LegacyFilterRow>
<ProjectOverviewFilters
project={projectId}
onChange={setTableState}
state={filterState}
)}
<ProjectFeaturesColumnsMenu
columnVisibility={columnVisibility}
environments={environments}
onToggle={onToggleColumnVisibility}
/>
<ProjectLifecycleFilters
projectId={projectId}
state={filterState}
onChange={setTableState}
total={loading ? undefined : total}
</FilterRow>
{showArchived ? null : (
<FilterRow>
<ProjectOverviewFilters
project={projectId}
onChange={setTableState}
state={filterState}
/>
</FilterRow>
)}
{isSmallScreen ? (
<ProjectFlagsSearch
searchQuery={tableState.query || ''}
onChangeSearchQuery={(query) => {
setTableState({ query });
}}
isLoading={loading}
/>
<ButtonGroup>
<PermissionIconButton
permission={UPDATE_FEATURE}
projectId={projectId}
onClick={() => setModalOpen(true)}
tooltipProps={{ title: 'Import' }}
data-testid={IMPORT_BUTTON}
data-loading-project
>
<ImportSvg />
</PermissionIconButton>
</ButtonGroup>
</LegacyFilterRow>
)}
) : null}
</FiltersContainer>
<SearchHighlightProvider value={tableState.query || ''}>
<PaginatedTable
tableInstance={table}

View File

@ -1,165 +0,0 @@
import { type ReactNode, type FC, useState } from 'react';
import {
Box,
Button,
IconButton,
Tooltip,
useMediaQuery,
useTheme,
} from '@mui/material';
import useLoading from 'hooks/useLoading';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Search } from 'component/common/Search/Search';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog';
import type { FeatureSchema } from 'openapi';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import ReviewsOutlined from '@mui/icons-material/ReviewsOutlined';
import { useFeedback } from 'component/feedbackNew/useFeedback';
import IosShare from '@mui/icons-material/IosShare';
import { FlagCreationButton } from './FlagCreationButton/FlagCreationButton.tsx';
interface IProjectFeatureTogglesHeaderProps {
isLoading?: boolean;
totalItems?: number;
searchQuery?: string;
onChangeSearchQuery?: (query: string) => void;
dataToExport?: Pick<FeatureSchema, 'name'>[];
environmentsToExport?: string[];
actions?: ReactNode;
}
/**
* @deprecated remove with `flagsUiFilterRefactor` flag
*/
export const ProjectFeatureTogglesHeader: FC<
IProjectFeatureTogglesHeaderProps
> = ({
isLoading,
totalItems,
searchQuery,
onChangeSearchQuery,
environmentsToExport,
actions,
}) => {
const projectId = useRequiredPathParam('projectId');
const headerLoadingRef = useLoading(isLoading || false);
const [showTitle, setShowTitle] = useState(true);
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
const [showExportDialog, setShowExportDialog] = useState(false);
const { trackEvent } = usePlausibleTracker();
const projectOverviewRefactorFeedback = false;
const { openFeedback } = useFeedback('newProjectOverview', 'automatic');
const handleSearch = (query: string) => {
onChangeSearchQuery?.(query);
trackEvent('search-bar', {
props: {
screen: 'project',
length: query.length,
},
});
};
const createFeedbackContext = () => {
openFeedback({
title: 'How easy was it to work with the project overview in Unleash?',
positiveLabel:
'What do you like most about the updated project overview?',
areasForImprovementsLabel:
'What improvements are needed in the project overview?',
});
};
return (
<Box ref={headerLoadingRef} aria-busy={isLoading} aria-live='polite'>
<PageHeader
titleElement={
showTitle
? `Feature flags ${
totalItems !== undefined ? `(${totalItems})` : ''
}`
: null
}
actions={
<>
<ConditionallyRender
condition={!isSmallScreen}
show={
<Search
data-loading
placeholder='Search and Filter'
expandable
initialValue={searchQuery || ''}
onChange={handleSearch}
onFocus={() => setShowTitle(false)}
onBlur={() => setShowTitle(true)}
hasFilters
id='projectFeatureFlags'
/>
}
/>
{actions}
<PageHeader.Divider sx={{ marginLeft: 0 }} />
<Tooltip title='Export all project flags' arrow>
<IconButton
data-loading
onClick={() => setShowExportDialog(true)}
sx={(theme) => ({
marginRight: theme.spacing(2),
})}
>
<IosShare />
</IconButton>
</Tooltip>
<ConditionallyRender
condition={!isLoading}
show={
<ExportDialog
showExportDialog={showExportDialog}
project={projectId}
data={[]}
onClose={() => setShowExportDialog(false)}
environments={environmentsToExport || []}
/>
}
/>
{/* FIXME: remove */}
<ConditionallyRender
condition={
projectOverviewRefactorFeedback &&
!isSmallScreen
}
show={
<Button
startIcon={<ReviewsOutlined />}
onClick={createFeedbackContext}
variant='outlined'
data-loading
>
Provide feedback
</Button>
}
/>
<FlagCreationButton isLoading={isLoading} />
</>
}
>
<ConditionallyRender
condition={isSmallScreen}
show={
<Search
initialValue={searchQuery || ''}
onChange={handleSearch}
hasFilters
id='projectFeatureFlags'
/>
}
/>
</PageHeader>
</Box>
);
};

View File

@ -3,8 +3,7 @@ import { useEffect } from 'react';
import type { FilterItemParamHolder } from '../../../filter/Filters/Filters.tsx';
import { useProjectStatus } from 'hooks/api/getters/useProjectStatus/useProjectStatus';
import { LifecycleFilters } from 'component/common/LifecycleFilters/LifecycleFilters.tsx';
import { Box, useMediaQuery, useTheme } from '@mui/material';
import { useUiFlag } from 'hooks/useUiFlag.ts';
import { Box, useTheme } from '@mui/material';
type ProjectLifecycleFiltersProps = {
projectId: string;
@ -23,8 +22,6 @@ export const ProjectLifecycleFilters: FC<ProjectLifecycleFiltersProps> = ({
}) => {
const { data } = useProjectStatus(projectId);
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
const flagsUiFilterRefactorEnabled = useUiFlag('flagsUiFilterRefactor');
const lifecycleSummary = Object.entries(
data?.lifecycleSummary || {},
).reduce(
@ -47,18 +44,7 @@ export const ProjectLifecycleFilters: FC<ProjectLifecycleFiltersProps> = ({
}
return (
<Box
sx={{
marginRight: 'auto',
...(flagsUiFilterRefactorEnabled
? {}
: {
margin: isSmallScreen
? theme.spacing(0, 3)
: `${theme.spacing(1.5)} auto ${theme.spacing(1.5)} ${theme.spacing(3)}`,
}),
}}
>
<Box sx={{ marginRight: 'auto' }}>
<LifecycleFilters
state={state}
onChange={onChange}

View File

@ -8,7 +8,6 @@ import {
import { useProjectFlagCreators } from 'hooks/api/getters/useProjectFlagCreators/useProjectFlagCreators';
import { formatTag } from 'utils/format-tag';
import { styled } from '@mui/material';
import { useUiFlag } from 'hooks/useUiFlag';
type ProjectOverviewFiltersProps = {
state: FilterItemParamHolder;
@ -28,10 +27,6 @@ export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({
const { tags } = useAllTags();
const { flagCreators } = useProjectFlagCreators(project);
const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
const flagsUiFilterRefactorEnabled = useUiFlag('flagsUiFilterRefactor');
const FilterComponent = flagsUiFilterRefactorEnabled
? StyledFilters
: Filters;
useEffect(() => {
const tagsOptions = (tags || []).map((tag) => {
@ -120,25 +115,13 @@ export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({
singularOperators: ['IS', 'IS_NOT'],
pluralOperators: ['IS_ANY_OF', 'IS_NONE_OF'],
},
...(!flagsUiFilterRefactorEnabled
? [
{
label: 'Show only archived',
icon: 'inventory',
options: [{ label: 'True', value: 'true' }],
filterKey: 'archived',
singularOperators: ['IS'],
pluralOperators: ['IS_ANY_OF'],
} satisfies IFilterItem,
]
: []),
];
setAvailableFilters(availableFilters);
}, [JSON.stringify(tags), JSON.stringify(flagCreators)]);
return (
<FilterComponent
<StyledFilters
availableFilters={availableFilters}
state={state}
onChange={onChange}

View File

@ -88,7 +88,6 @@ export type UiFlags = {
lifecycleGraphs?: boolean;
newStrategyModal?: boolean;
globalChangeRequestList?: boolean;
flagsUiFilterRefactor?: boolean;
trafficBillingDisplay?: boolean;
milestoneProgression?: boolean;
featureReleasePlans?: boolean;

View File

@ -60,7 +60,6 @@ export type IFlagKey =
| 'newStrategyModal'
| 'globalChangeRequestList'
| 'newUiConfigService'
| 'flagsUiFilterRefactor'
| 'trafficBillingDisplay'
| 'milestoneProgression'
| 'envAddStrategySuggestion'
@ -277,10 +276,6 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_NEW_UI_CONFIG_SERVICE,
false,
),
flagsUiFilterRefactor: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_FLAGS_UI_FILTER_REFACTOR,
false,
),
trafficBillingDisplay: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_TRAFFIC_BILLING_DISPLAY,
false,

View File

@ -56,7 +56,6 @@ process.nextTick(async () => {
newStrategyModal: true,
globalChangeRequestList: true,
newUiConfigService: true,
flagsUiFilterRefactor: true,
trafficBillingDisplay: true,
milestoneProgression: true,
featureReleasePlans: true,