mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-04 13:48:56 +02:00
chore: add project list view toggle with respective flag (#10452)
https://linear.app/unleash/issue/2-3746/add-project-list-view-toggle-with-respective-flag Adds a project list view toggle hidden behind a feature flag: `projectListViewToggle`. This is already part of the persistent project list page state. Even though the view mode switching logic is in place, this isn't really doing anything else. We'll leave the actual visual changes (tables) for a follow up PR. <img width="1412" height="406" alt="image" src="https://github.com/user-attachments/assets/793d0bd9-9874-4630-98b4-0ee364f50241" />
This commit is contained in:
parent
91f138349e
commit
bd5a8539c0
@ -19,6 +19,8 @@ import { ProjectArchiveLink } from './ProjectArchiveLink/ProjectArchiveLink.tsx'
|
||||
import { ProjectsListHeader } from './ProjectsListHeader/ProjectsListHeader.tsx';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { TablePlaceholder } from 'component/common/Table/index.ts';
|
||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
|
||||
import { ProjectsListViewToggle } from './ProjectsListViewToggle/ProjectsListViewToggle.tsx';
|
||||
|
||||
const StyledApiError = styled(ApiError)(({ theme }) => ({
|
||||
maxWidth: '500px',
|
||||
@ -34,6 +36,7 @@ const StyledContainer = styled('div')(({ theme }) => ({
|
||||
export const ProjectList = () => {
|
||||
const { projects, loading, error, refetch } = useProjects();
|
||||
const { isOss } = useUiConfig();
|
||||
const projectListViewToggleEnabled = useUiFlag('projectListViewToggle');
|
||||
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
@ -126,14 +129,25 @@ export const ProjectList = () => {
|
||||
<ProjectsListHeader
|
||||
helpText='Favorite projects, projects you own, and projects you are a member of'
|
||||
actions={
|
||||
<ProjectsListSort
|
||||
sortBy={state.sortBy}
|
||||
setSortBy={(sortBy) =>
|
||||
setState({
|
||||
sortBy: sortBy as typeof state.sortBy,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<>
|
||||
{projectListViewToggleEnabled &&
|
||||
!isOss() && (
|
||||
<ProjectsListViewToggle
|
||||
view={state.view}
|
||||
setView={(view) =>
|
||||
setState({ view })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<ProjectsListSort
|
||||
sortBy={state.sortBy}
|
||||
setSortBy={(sortBy) =>
|
||||
setState({
|
||||
sortBy: sortBy as typeof state.sortBy,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
>
|
||||
My projects
|
||||
|
@ -25,6 +25,13 @@ const StyledHeaderTitle = styled('div')(({ theme }) => ({
|
||||
flexGrow: 0,
|
||||
}));
|
||||
|
||||
const StyledHeaderActions = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginLeft: 'auto',
|
||||
gap: theme.spacing(2),
|
||||
}));
|
||||
|
||||
export const ProjectsListHeader: FC<ProjectsListHeaderProps> = ({
|
||||
children,
|
||||
helpText,
|
||||
@ -36,7 +43,7 @@ export const ProjectsListHeader: FC<ProjectsListHeaderProps> = ({
|
||||
{children}
|
||||
<HelpIcon tooltip={helpText} />
|
||||
</StyledHeaderTitle>
|
||||
{actions}
|
||||
<StyledHeaderActions>{actions}</StyledHeaderActions>
|
||||
</StyledHeaderContainer>
|
||||
);
|
||||
};
|
||||
|
@ -2,12 +2,6 @@ import type { FC } from 'react';
|
||||
import GeneralSelect from 'component/common/GeneralSelect/GeneralSelect';
|
||||
import { styled } from '@mui/material';
|
||||
|
||||
const StyledWrapper = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
flex: 1,
|
||||
}));
|
||||
|
||||
const StyledContainer = styled('div')(() => ({
|
||||
maxWidth: '200px',
|
||||
width: '100%',
|
||||
@ -35,16 +29,14 @@ export const ProjectsListSort: FC<ProjectsListSortProps> = ({
|
||||
setSortBy,
|
||||
}) => {
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<StyledContainer>
|
||||
<GeneralSelect
|
||||
fullWidth
|
||||
label='Sort by'
|
||||
onChange={setSortBy}
|
||||
options={options}
|
||||
value={sortBy || options[0].key}
|
||||
/>
|
||||
</StyledContainer>
|
||||
</StyledWrapper>
|
||||
<StyledContainer>
|
||||
<GeneralSelect
|
||||
fullWidth
|
||||
label='Sort by'
|
||||
onChange={setSortBy}
|
||||
options={options}
|
||||
value={sortBy || options[0].key}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,28 @@
|
||||
import { IconButton, Tooltip } from '@mui/material';
|
||||
import type { ProjectsListView } from '../hooks/useProjectsListState.js';
|
||||
import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted';
|
||||
import GridViewIcon from '@mui/icons-material/GridView';
|
||||
|
||||
type ProjectsListViewToggleProps = {
|
||||
view: ProjectsListView;
|
||||
setView: (view: ProjectsListView) => void;
|
||||
};
|
||||
|
||||
export const ProjectsListViewToggle = ({
|
||||
view,
|
||||
setView,
|
||||
}: ProjectsListViewToggleProps) => {
|
||||
const nextView = view === 'list' ? 'cards' : 'list';
|
||||
|
||||
return (
|
||||
<Tooltip title={`Switch to ${nextView} view`} arrow>
|
||||
<IconButton size='small' onClick={() => setView(nextView)}>
|
||||
{nextView === 'list' ? (
|
||||
<FormatListBulletedIcon />
|
||||
) : (
|
||||
<GridViewIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
@ -7,6 +7,8 @@ import {
|
||||
} from 'use-query-params';
|
||||
import { sortKeys } from '../ProjectsListSort/ProjectsListSort.jsx';
|
||||
|
||||
export type ProjectsListView = 'cards' | 'list';
|
||||
|
||||
const stateConfig = {
|
||||
query: StringParam,
|
||||
sortBy: withDefault(
|
||||
@ -14,6 +16,10 @@ const stateConfig = {
|
||||
sortKeys[0],
|
||||
) as QueryParamConfig<(typeof sortKeys)[number] | null | undefined>,
|
||||
create: StringParam,
|
||||
view: withDefault(
|
||||
createEnumParam<ProjectsListView>(['cards', 'list']),
|
||||
'cards',
|
||||
) as QueryParamConfig<ProjectsListView>,
|
||||
} as const;
|
||||
|
||||
export const useProjectsListState = () =>
|
||||
|
@ -95,6 +95,7 @@ export type UiFlags = {
|
||||
reportUnknownFlags?: boolean;
|
||||
lifecycleGraphs?: boolean;
|
||||
addConfiguration?: boolean;
|
||||
projectListViewToggle?: boolean;
|
||||
};
|
||||
|
||||
export interface IVersionInfo {
|
||||
|
@ -65,7 +65,8 @@ export type IFlagKey =
|
||||
| 'timestampsInChangeRequestTimeline'
|
||||
| 'lifecycleGraphs'
|
||||
| 'githubAuth'
|
||||
| 'addConfiguration';
|
||||
| 'addConfiguration'
|
||||
| 'projectListViewToggle';
|
||||
|
||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||
|
||||
@ -305,6 +306,10 @@ const flags: IFlags = {
|
||||
process.env.UNLEASH_EXPERIMENTAL_ADD_CONFIGURATION,
|
||||
false,
|
||||
),
|
||||
projectListViewToggle: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_PROJECT_LIST_VIEW_TOGGLE,
|
||||
false,
|
||||
),
|
||||
};
|
||||
|
||||
export const defaultExperimentalOptions: IExperimentalOptions = {
|
||||
|
@ -61,6 +61,7 @@ process.nextTick(async () => {
|
||||
timestampsInChangeRequestTimeline: true,
|
||||
lifecycleGraphs: true,
|
||||
addConfiguration: true,
|
||||
projectListViewToggle: true,
|
||||
},
|
||||
},
|
||||
authentication: {
|
||||
|
Loading…
Reference in New Issue
Block a user