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

feat: improve flag filters on project page (#10705)

This commit is contained in:
Tymoteusz Czech 2025-10-01 10:11:02 +02:00 committed by GitHub
parent c12aca72db
commit 9d996f14d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 372 additions and 192 deletions

View File

@ -37,7 +37,6 @@ interface ILifecycleFiltersBaseProps {
const Wrapper = styled(Box)(({ theme }) => ({ const Wrapper = styled(Box)(({ theme }) => ({
display: 'flex', display: 'flex',
justifyContent: 'space-between', justifyContent: 'space-between',
minHeight: theme.spacing(7),
gap: theme.spacing(2), gap: theme.spacing(2),
})); }));

View File

@ -20,7 +20,7 @@ import {
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled'; import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { useFeatureToggleSwitch } from '../ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch.tsx'; import { useFeatureToggleSwitch } from '../ProjectFeatureToggles/FeatureToggleSwitch/useFeatureToggleSwitch.tsx';
import useLoading from 'hooks/useLoading'; import useLoading from 'hooks/useLoading';
import { ProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/ProjectFeatureTogglesHeader.tsx'; import { ProjectFeatureTogglesHeader as LegacyProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/LegacyProjectFeatureTogglesHeader.tsx';
import { createColumnHelper, useReactTable } from '@tanstack/react-table'; import { createColumnHelper, useReactTable } from '@tanstack/react-table';
import { withTableState } from 'utils/withTableState'; import { withTableState } from 'utils/withTableState';
import type { FeatureSearchResponseSchema } from 'openapi'; import type { FeatureSearchResponseSchema } from 'openapi';
@ -41,7 +41,7 @@ import {
useProjectFeatureSearchActions, useProjectFeatureSearchActions,
} from './useProjectFeatureSearch.ts'; } from './useProjectFeatureSearch.ts';
import { AvatarCell } from './AvatarCell.tsx'; import { AvatarCell } from './AvatarCell.tsx';
import { styled } from '@mui/material'; import { styled, useMediaQuery, useTheme } from '@mui/material';
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview'; import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
import { ConnectSdkDialog } from '../../../onboarding/dialog/ConnectSdkDialog.tsx'; import { ConnectSdkDialog } from '../../../onboarding/dialog/ConnectSdkDialog.tsx';
import { ProjectOnboarding } from '../../../onboarding/flow/ProjectOnboarding.tsx'; import { ProjectOnboarding } from '../../../onboarding/flow/ProjectOnboarding.tsx';
@ -57,6 +57,9 @@ import { IMPORT_BUTTON } from 'utils/testIds';
import { ProjectCleanupReminder } from './ProjectCleanupReminder/ProjectCleanupReminder.tsx'; import { ProjectCleanupReminder } from './ProjectCleanupReminder/ProjectCleanupReminder.tsx';
import { formatEnvironmentColumnId } from './formatEnvironmentColumnId.ts'; import { formatEnvironmentColumnId } from './formatEnvironmentColumnId.ts';
import { ProjectFeaturesColumnsMenu } from './ProjectFeaturesColumnsMenu/ProjectFeaturesColumnsMenu.tsx'; import { ProjectFeaturesColumnsMenu } from './ProjectFeaturesColumnsMenu/ProjectFeaturesColumnsMenu.tsx';
import { useUiFlag } from 'hooks/useUiFlag.ts';
import { ProjectFeatureTogglesHeader } from './ProjectFeatureTogglesHeader/ProjectFeatureTogglesHeader.tsx';
import { ProjectFlagsSearch } from './ProjectFlagsSearch/ProjectFlagsSearch.tsx';
type ProjectFeatureTogglesProps = { type ProjectFeatureTogglesProps = {
environments: string[]; environments: string[];
@ -71,12 +74,27 @@ const Container = styled('div')(({ theme }) => ({
gap: theme.spacing(2), gap: theme.spacing(2),
})); }));
const FilterRow = styled('div')(({ theme }) => ({ const LegacyFilterRow = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
flexFlow: 'row wrap', flexFlow: 'row wrap',
justifyContent: 'space-between', justifyContent: 'space-between',
})); }));
const FiltersContainer = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'column',
gap: theme.spacing(1),
padding: theme.spacing(2, 3, 2),
[theme.breakpoints.down('md')]: {
padding: theme.spacing(2, 2),
},
}));
const FilterRow = styled('div')({
display: 'flex',
alignItems: 'center',
});
const ButtonGroup = styled('div')(({ theme }) => ({ const ButtonGroup = styled('div')(({ theme }) => ({
display: 'flex', display: 'flex',
gap: theme.spacing(1), gap: theme.spacing(1),
@ -91,6 +109,9 @@ export const ProjectFeatureToggles = ({
const { project } = useProjectOverview(projectId); const { project } = useProjectOverview(projectId);
const [connectSdkOpen, setConnectSdkOpen] = useState(false); const [connectSdkOpen, setConnectSdkOpen] = useState(false);
const [modalOpen, setModalOpen] = useState(false); const [modalOpen, setModalOpen] = useState(false);
const flagsUiFilterRefactorEnabled = useUiFlag('flagsUiFilterRefactor');
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
const { const {
features, features,
@ -500,25 +521,32 @@ export const ProjectFeatureToggles = ({
) : null} ) : null}
<PageContent <PageContent
disableLoading disableLoading
disablePadding
header={ header={
<ProjectFeatureTogglesHeader flagsUiFilterRefactorEnabled ? (
isLoading={initialLoad} <ProjectFeatureTogglesHeader
totalItems={total} isLoading={initialLoad}
searchQuery={tableState.query || ''} totalItems={total}
onChangeSearchQuery={(query) => { environmentsToExport={environments}
setTableState({ query }); />
}} ) : (
dataToExport={data} <LegacyProjectFeatureTogglesHeader
environmentsToExport={environments} isLoading={initialLoad}
actions={ totalItems={total}
<ProjectFeaturesColumnsMenu searchQuery={tableState.query || ''}
columnVisibility={columnVisibility} onChangeSearchQuery={(query) => {
environments={environments} setTableState({ query });
onToggle={onToggleColumnVisibility} }}
/> dataToExport={data}
} environmentsToExport={environments}
/> actions={
<ProjectFeaturesColumnsMenu
columnVisibility={columnVisibility}
environments={environments}
onToggle={onToggleColumnVisibility}
/>
}
/>
)
} }
bodyClass='noop' bodyClass='noop'
style={{ cursor: 'inherit' }} style={{ cursor: 'inherit' }}
@ -528,31 +556,74 @@ export const ProjectFeatureToggles = ({
aria-busy={isPlaceholder} aria-busy={isPlaceholder}
aria-live='polite' aria-live='polite'
> >
<FilterRow> {flagsUiFilterRefactorEnabled ? (
<ProjectOverviewFilters <FiltersContainer>
project={projectId} <FilterRow>
onChange={setTableState} <ProjectLifecycleFilters
state={filterState} projectId={projectId}
/> state={filterState}
<ProjectLifecycleFilters onChange={setTableState}
projectId={projectId} total={loading ? undefined : total}
state={filterState} />
onChange={setTableState} {isSmallScreen ? null : (
total={loading ? undefined : total} <ProjectFlagsSearch
/> searchQuery={tableState.query || ''}
<ButtonGroup> onChangeSearchQuery={(query) => {
<PermissionIconButton setTableState({ query });
permission={UPDATE_FEATURE} }}
isLoading={loading}
/>
)}
<ProjectFeaturesColumnsMenu
columnVisibility={columnVisibility}
environments={environments}
onToggle={onToggleColumnVisibility}
/>
</FilterRow>
<FilterRow>
<ProjectOverviewFilters
project={projectId}
onChange={setTableState}
state={filterState}
/>
</FilterRow>
{isSmallScreen ? (
<ProjectFlagsSearch
searchQuery={tableState.query || ''}
onChangeSearchQuery={(query) => {
setTableState({ query });
}}
isLoading={loading}
/>
) : null}
</FiltersContainer>
) : (
<LegacyFilterRow>
<ProjectOverviewFilters
project={projectId}
onChange={setTableState}
state={filterState}
/>
<ProjectLifecycleFilters
projectId={projectId} projectId={projectId}
onClick={() => setModalOpen(true)} state={filterState}
tooltipProps={{ title: 'Import' }} onChange={setTableState}
data-testid={IMPORT_BUTTON} total={loading ? undefined : total}
data-loading-project />
> <ButtonGroup>
<ImportSvg /> <PermissionIconButton
</PermissionIconButton> permission={UPDATE_FEATURE}
</ButtonGroup> projectId={projectId}
</FilterRow> onClick={() => setModalOpen(true)}
tooltipProps={{ title: 'Import' }}
data-testid={IMPORT_BUTTON}
data-loading-project
>
<ImportSvg />
</PermissionIconButton>
</ButtonGroup>
</LegacyFilterRow>
)}
<SearchHighlightProvider value={tableState.query || ''}> <SearchHighlightProvider value={tableState.query || ''}>
<PaginatedTable <PaginatedTable
tableInstance={table} tableInstance={table}

View File

@ -0,0 +1,165 @@
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

@ -1,136 +1,59 @@
import { type ReactNode, type FC, useState } from 'react'; import { type FC, useState } from 'react';
import { import { Box, IconButton, Tooltip } from '@mui/material';
Box,
IconButton,
Tooltip,
useMediaQuery,
useTheme,
} from '@mui/material';
import useLoading from 'hooks/useLoading'; import useLoading from 'hooks/useLoading';
import { PageHeader } from 'component/common/PageHeader/PageHeader'; 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 { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog'; import { ExportDialog } from 'component/feature/FeatureToggleList/ExportDialog';
import type { FeatureSchema } from 'openapi';
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
import IosShare from '@mui/icons-material/IosShare'; import IosShare from '@mui/icons-material/IosShare';
import { FlagCreationButton } from './FlagCreationButton/FlagCreationButton.tsx'; import { FlagCreationButton } from './FlagCreationButton/FlagCreationButton.tsx';
import { ImportButton } from './ImportButton/ImportButton.tsx';
interface IProjectFeatureTogglesHeaderProps { type ProjectFeatureTogglesHeaderProps = {
isLoading?: boolean; isLoading?: boolean;
totalItems?: number; totalItems?: number;
searchQuery?: string;
onChangeSearchQuery?: (query: string) => void;
dataToExport?: Pick<FeatureSchema, 'name'>[];
environmentsToExport?: string[]; environmentsToExport?: string[];
actions?: ReactNode; };
}
export const ProjectFeatureTogglesHeader: FC< export const ProjectFeatureTogglesHeader: FC<
IProjectFeatureTogglesHeaderProps ProjectFeatureTogglesHeaderProps
> = ({ > = ({ isLoading, totalItems, environmentsToExport }) => {
isLoading,
totalItems,
searchQuery,
onChangeSearchQuery,
environmentsToExport,
actions,
}) => {
const projectId = useRequiredPathParam('projectId'); const projectId = useRequiredPathParam('projectId');
const headerLoadingRef = useLoading(isLoading || false); 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 [showExportDialog, setShowExportDialog] = useState(false);
const { trackEvent } = usePlausibleTracker();
const handleSearch = (query: string) => {
onChangeSearchQuery?.(query);
trackEvent('search-bar', {
props: {
screen: 'project',
length: query.length,
},
});
};
return ( return (
<Box <Box ref={headerLoadingRef} aria-busy={isLoading} aria-live='polite'>
ref={headerLoadingRef}
aria-busy={isLoading}
aria-live='polite'
sx={(theme) => ({
padding: `${theme.spacing(2.5)} ${theme.spacing(3.125)}`,
})}
>
<PageHeader <PageHeader
titleElement={ titleElement={`Feature flags ${
showTitle totalItems !== undefined ? `(${totalItems})` : ''
? `Feature flags ${ }`}
totalItems !== undefined ? `(${totalItems})` : ''
}`
: null
}
actions={ 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> <Tooltip title='Export all project flags' arrow>
<IconButton <IconButton
data-loading data-loading
onClick={() => setShowExportDialog(true)} onClick={() => setShowExportDialog(true)}
sx={(theme) => ({
marginRight: theme.spacing(2),
})}
> >
<IosShare /> <IosShare />
</IconButton> </IconButton>
</Tooltip> </Tooltip>
<ImportButton />
<ConditionallyRender {!isLoading ? (
condition={!isLoading} <ExportDialog
show={ showExportDialog={showExportDialog}
<ExportDialog project={projectId}
showExportDialog={showExportDialog} data={[]}
project={projectId} onClose={() => setShowExportDialog(false)}
data={[]} environments={environmentsToExport || []}
onClose={() => setShowExportDialog(false)} />
environments={environmentsToExport || []} ) : null}
/> <Box>
} <FlagCreationButton isLoading={isLoading} />
/> </Box>
<FlagCreationButton isLoading={isLoading} />
</> </>
} }
> />
<ConditionallyRender
condition={isSmallScreen}
show={
<Search
initialValue={searchQuery || ''}
onChange={handleSearch}
hasFilters
id='projectFeatureFlags'
/>
}
/>
</PageHeader>
</Box> </Box>
); );
}; };

View File

@ -1,4 +1,5 @@
import type { FC } from 'react'; import type { FC } from 'react';
import { Box } from '@mui/material';
import { ColumnsMenu } from '../ColumnsMenu/ColumnsMenu.tsx'; import { ColumnsMenu } from '../ColumnsMenu/ColumnsMenu.tsx';
import { formatEnvironmentColumnId } from '../formatEnvironmentColumnId.ts'; import { formatEnvironmentColumnId } from '../formatEnvironmentColumnId.ts';
@ -12,47 +13,49 @@ export const ProjectFeaturesColumnsMenu: FC<
ProjectFeaturesColumnsMenuProps ProjectFeaturesColumnsMenuProps
> = ({ columnVisibility, environments, onToggle }) => { > = ({ columnVisibility, environments, onToggle }) => {
return ( return (
<ColumnsMenu <Box sx={(theme) => ({ marginLeft: theme.spacing(1) })}>
columns={[ <ColumnsMenu
{ columns={[
header: 'Name', {
id: 'name', header: 'Name',
isVisible: columnVisibility.name, id: 'name',
isStatic: true, isVisible: columnVisibility.name,
}, isStatic: true,
{ },
header: 'Created', {
id: 'createdAt', header: 'Created',
isVisible: columnVisibility.createdAt, id: 'createdAt',
}, isVisible: columnVisibility.createdAt,
{ },
header: 'By', {
id: 'createdBy', header: 'By',
isVisible: columnVisibility.createdBy, id: 'createdBy',
}, isVisible: columnVisibility.createdBy,
{ },
header: 'Last seen', {
id: 'lastSeenAt', header: 'Last seen',
isVisible: columnVisibility.lastSeenAt, id: 'lastSeenAt',
}, isVisible: columnVisibility.lastSeenAt,
{ },
header: 'Lifecycle', {
id: 'lifecycle', header: 'Lifecycle',
isVisible: columnVisibility.lifecycle, id: 'lifecycle',
}, isVisible: columnVisibility.lifecycle,
{ },
id: 'divider', {
}, id: 'divider',
...environments.map((environment) => ({ },
header: environment, ...environments.map((environment) => ({
id: formatEnvironmentColumnId(environment), header: environment,
isVisible: id: formatEnvironmentColumnId(environment),
columnVisibility[ isVisible:
formatEnvironmentColumnId(environment) columnVisibility[
], formatEnvironmentColumnId(environment)
})), ],
]} })),
onToggle={onToggle} ]}
/> onToggle={onToggle}
/>
</Box>
); );
}; };

View File

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

View File

@ -7,6 +7,8 @@ import {
} from 'component/filter/Filters/Filters'; } from 'component/filter/Filters/Filters';
import { useProjectFlagCreators } from 'hooks/api/getters/useProjectFlagCreators/useProjectFlagCreators'; import { useProjectFlagCreators } from 'hooks/api/getters/useProjectFlagCreators/useProjectFlagCreators';
import { formatTag } from 'utils/format-tag'; import { formatTag } from 'utils/format-tag';
import { styled } from '@mui/material';
import { useUiFlag } from 'hooks/useUiFlag';
type ProjectOverviewFiltersProps = { type ProjectOverviewFiltersProps = {
state: FilterItemParamHolder; state: FilterItemParamHolder;
@ -14,6 +16,10 @@ type ProjectOverviewFiltersProps = {
project: string; project: string;
}; };
const StyledFilters = styled(Filters)({
padding: 0,
});
export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({ export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({
state, state,
onChange, onChange,
@ -22,6 +28,10 @@ export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({
const { tags } = useAllTags(); const { tags } = useAllTags();
const { flagCreators } = useProjectFlagCreators(project); const { flagCreators } = useProjectFlagCreators(project);
const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]); const [availableFilters, setAvailableFilters] = useState<IFilterItem[]>([]);
const flagsUiFilterRefactorEnabled = useUiFlag('flagsUiFilterRefactor');
const FilterComponent = flagsUiFilterRefactorEnabled
? StyledFilters
: Filters;
useEffect(() => { useEffect(() => {
const tagsOptions = (tags || []).map((tag) => { const tagsOptions = (tags || []).map((tag) => {
@ -124,7 +134,7 @@ export const ProjectOverviewFilters: FC<ProjectOverviewFiltersProps> = ({
}, [JSON.stringify(tags), JSON.stringify(flagCreators)]); }, [JSON.stringify(tags), JSON.stringify(flagCreators)]);
return ( return (
<Filters <FilterComponent
availableFilters={availableFilters} availableFilters={availableFilters}
state={state} state={state}
onChange={onChange} onChange={onChange}

View File

@ -88,6 +88,7 @@ export type UiFlags = {
lifecycleGraphs?: boolean; lifecycleGraphs?: boolean;
newStrategyModal?: boolean; newStrategyModal?: boolean;
globalChangeRequestList?: boolean; globalChangeRequestList?: boolean;
flagsUiFilterRefactor?: boolean;
}; };
export interface IVersionInfo { export interface IVersionInfo {