1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

Project features list update (#991)

* refactor: column icon position

* project overview horizontal scroll

* updated table headers styles

* fix: feature overview switch title

* refactor: cleanup of sortable header styles

* fix: z-index issue in test

* fix: html semantics after review
This commit is contained in:
Tymoteusz Czech 2022-05-18 11:56:55 +02:00 committed by GitHub
parent 98b6214c28
commit 06b0a29ea8
27 changed files with 132 additions and 75 deletions

View File

@ -2,6 +2,7 @@ import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
rolesListBody: {
padding: theme.spacing(4),
paddingBottom: '4rem',
minHeight: '50vh',
position: 'relative',

View File

@ -1,7 +1,8 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(() => ({
export const useStyles = makeStyles()(theme => ({
userListBody: {
padding: theme.spacing(4),
paddingBottom: '4rem',
minHeight: '50vh',
position: 'relative',

View File

@ -15,9 +15,9 @@ export const useStyles = makeStyles()(theme => ({
},
},
bodyContainer: {
padding: '2rem',
padding: theme.spacing(4),
[theme.breakpoints.down('md')]: {
padding: '1rem',
padding: theme.spacing(2),
},
},
paddingDisabled: {

View File

@ -38,11 +38,13 @@ export const PageContent: FC<IPageContentProps> = ({
[styles.borderDisabled]: disableBorder,
});
const bodyClasses = classnames(styles.bodyContainer, {
[styles.paddingDisabled]: disablePadding,
[styles.borderDisabled]: disableBorder,
[bodyClass]: bodyClass,
});
const bodyClasses = classnames(
bodyClass ? bodyClass : styles.bodyContainer,
{
[styles.paddingDisabled]: disablePadding,
[styles.borderDisabled]: disableBorder,
}
);
const paperProps = disableBorder ? { elevation: 0 } : {};

View File

@ -10,7 +10,12 @@ export const useStyles = makeStyles()(theme => ({
justifyContent: 'space-between',
alignItems: 'center',
position: 'relative',
flexWrap: 'wrap',
},
header: {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
marginRight: theme.spacing(2),
},
headerTitle: {
fontSize: theme.fontSizes.mainHeader,
@ -21,6 +26,7 @@ export const useStyles = makeStyles()(theme => ({
flexGrow: 1,
justifyContent: 'flex-end',
alignItems: 'center',
gap: theme.spacing(1),
},
verticalSeparator: {
height: '100%',

View File

@ -35,7 +35,10 @@ const PageHeaderComponent: FC<IPageHeaderProps> & { Divider: VFC } = ({
return (
<div className={styles.headerContainer}>
<div className={styles.topContainer}>
<div className={headerClasses} data-loading>
<div
className={classnames(styles.header, headerClasses)}
data-loading
>
<Typography
variant={variant || 'h1'}
className={classnames(styles.headerTitle, className)}

View File

@ -19,11 +19,17 @@ export const useStyles = makeStyles()(theme => ({
padding: theme.spacing(2),
whiteSpace: 'nowrap',
width: '100%',
'& .hover-only': {
visibility: 'hidden',
},
':hover, :focus, &:focus-visible, &:active': {
outline: 'revert',
'& svg': {
color: 'inherit',
},
'& .hover-only': {
visibility: 'visible',
},
},
display: 'flex',
alignItems: 'center',

View File

@ -41,7 +41,7 @@ export const SortArrow: VFC<ISortArrowProps> = ({
}
elseShow={
<UnfoldMoreOutlined
className={styles.icon}
className={classnames(styles.icon, 'hover-only')}
fontSize="inherit"
/>
}

View File

@ -15,7 +15,7 @@ interface ITableActionsProps {
}
/**
* @deprecated
* @deprecated Use <PageHeader actions={} /> instead
*/
export const TableActions: FC<ITableActionsProps> = ({
initialSearchValue: search,

View File

@ -1,9 +1,15 @@
import { FC } from 'react';
import classnames from 'classnames';
import { TableCell as MUITableCell, TableCellProps } from '@mui/material';
import { useStyles } from './TableCell.styles';
export const TableCell: FC<TableCellProps> = ({ ...props }) => {
export const TableCell: FC<TableCellProps> = ({ className, ...props }) => {
const { classes: styles } = useStyles();
return <MUITableCell className={styles.tableCell} {...props} />;
return (
<MUITableCell
className={classnames(styles.tableCell, className)}
{...props}
/>
);
};

View File

@ -37,7 +37,7 @@ const FeatureOverviewEnvSwitch = ({
await toggleFeatureEnvironmentOn(projectId, featureId, env.name);
setToastData({
type: 'success',
title: 'Available in production',
title: `Available in ${env.name}`,
text: `${featureId} is now available in ${env.name} based on its defined strategies.`,
});
refetchFeature();
@ -61,7 +61,7 @@ const FeatureOverviewEnvSwitch = ({
await toggleFeatureEnvironmentOff(projectId, featureId, env.name);
setToastData({
type: 'success',
title: 'Unavailable in production',
title: `Unavailable in ${env.name}`,
text: `${featureId} is unavailable in ${env.name} and its strategies will no longer have any effect.`,
});
refetchFeature();

View File

@ -8,7 +8,10 @@ export const useStyles = makeStyles()(theme => ({
flexDirection: 'column',
},
},
projectToggles: { width: '100%', minHeight: '100%' },
projectToggles: {
width: '100%',
minWidth: 0,
},
header: {
backgroundColor: '#fff',
borderRadius: theme.shape.borderRadiusLarge,
@ -36,13 +39,15 @@ export const useStyles = makeStyles()(theme => ({
},
},
title: {
margin: 0,
width: '100%',
fontSize: theme.fontSizes.mainHeader,
fontWeight: 'bold',
marginBottom: '0.5rem',
display: 'grid',
gridTemplateColumns: '1fr auto',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gridGap: '1rem',
gap: '1rem',
},
titleText: {
overflow: 'hidden',

View File

@ -90,7 +90,6 @@ const Project = () => {
return tabData.map((tab, index) => {
return (
<Tab
data-loading
key={tab.title}
id={`tab-${index}`}
aria-controls={`tabpanel-${index}`}
@ -116,12 +115,10 @@ const Project = () => {
<div ref={ref}>
<div className={styles.header}>
<div className={styles.innerContainer}>
<h2
data-loading
className={styles.title}
style={{ margin: 0, width: '100%' }}
>
<div className={styles.titleText}>{project?.name}</div>
<h2 className={styles.title}>
<div className={styles.titleText} data-loading>
{project?.name || projectId}
</div>
<PermissionIconButton
permission={UPDATE_PROJECT}
projectId={project?.id}

View File

@ -1,9 +1,15 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
cell: {
display: 'flex',
justifyContent: 'center',
paddingRight: theme.spacing(2),
},
menuContainer: {
borderRadius: theme.shape.borderRadiusLarge,
padding: theme.spacing(1),
paddingRight: theme.spacing(3),
},
item: {
borderRadius: theme.shape.borderRadius,

View File

@ -57,7 +57,7 @@ export const ActionsCell: VFC<IActionsCellProps> = ({ projectId, row }) => {
const menuId = `${id}-menu`;
return (
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<Box className={classes.cell}>
<Tooltip
title="Feature toggle actions"
arrow

View File

@ -17,12 +17,11 @@ import {
import ViewColumnIcon from '@mui/icons-material/ViewColumn';
import CloseIcon from '@mui/icons-material/Close';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { capitalize } from 'lodash';
import { useStyles } from './ColumnsMenu.styles';
interface IColumnsMenuProps {
allColumns: {
Header: string | any;
Header?: string | any;
id: string;
isVisible: boolean;
toggleHidden: (state: boolean) => void;
@ -104,6 +103,7 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
onClick={handleClick}
type="button"
className={classes.button}
data-loading
>
<ViewColumnIcon />
</IconButton>
@ -165,14 +165,12 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
primary={
<Typography variant="body2">
<ConditionallyRender
condition={
condition={Boolean(
typeof column.Header ===
'string'
}
'string' && column.Header
)}
show={() => <>{column.Header}</>}
elseShow={() =>
capitalize(column.id)
}
elseShow={() => column.id}
/>
</Typography>
}

View File

@ -43,12 +43,13 @@ export const FeatureToggleSwitch: VFC<IFeatureToggleSwitchProps> = ({
key={`${featureName}-${environmentName}`} // Prevent animation when archiving rows
>
<PermissionSwitch
checked={isChecked}
checked={value}
environmentId={environmentName}
projectId={projectId}
permission={UPDATE_FEATURE_ENVIRONMENT}
inputProps={{ 'aria-label': environmentName }}
onClick={onClick}
disabled={isChecked !== value}
/>
</Box>
);

View File

@ -7,7 +7,6 @@ export const useStyles = makeStyles()(theme => ({
minHeight: '100%',
width: 'calc(100% - 1rem)',
position: 'relative',
paddingBottom: '4rem',
[theme.breakpoints.down('md')]: {
marginLeft: '0',
paddingBottom: '4rem',
@ -25,7 +24,10 @@ export const useStyles = makeStyles()(theme => ({
},
},
},
bodyClass: { padding: '0.5rem 1rem' },
bodyClass: {
overflowX: 'auto',
padding: theme.spacing(4),
},
header: {
padding: '1rem',
},

View File

@ -70,13 +70,15 @@ export const ProjectFeatureToggles = ({
const projectId = useRequiredPathParam('projectId');
const navigate = useNavigate();
const { uiConfig } = useUiConfig();
const environments = useEnvironmentsRef(newEnvironments);
const environments = useEnvironmentsRef(
loading ? ['a', 'b', 'c'] : newEnvironments
);
const { refetch } = useProject(projectId);
const { setToastData, setToastApiError } = useToast();
const data = useMemo<ListItemType[]>(() => {
if (loading) {
return Array(12).fill({
return Array(6).fill({
type: '-',
name: 'Feature name',
createdAt: new Date(),
@ -180,7 +182,8 @@ export const ProjectFeatureToggles = ({
/>
),
width: '99%',
minWdith: 100,
minWidth: 100,
maxWidth: 200,
sortType: 'alphanumeric',
},
{
@ -191,9 +194,9 @@ export const ProjectFeatureToggles = ({
align: 'center',
},
...environments.map(name => ({
Header: name,
maxWidth: 103,
minWidth: 103,
Header: loading ? () => '' : name,
maxWidth: 90,
minWidth: 90,
accessor: `environments.${name}`,
align: 'center',
Cell: ({
@ -218,25 +221,16 @@ export const ProjectFeatureToggles = ({
},
})),
{
Header: ({ allColumns, setHiddenColumns }: any) => (
<ColumnsMenu
allColumns={allColumns}
staticColumns={['actions', 'name']}
dividerAfter={['createdAt']}
dividerBefore={['actions']}
setHiddenColumns={setHiddenColumns}
/>
),
maxWidth: 60,
width: 60,
id: 'actions',
id: 'Actions',
maxWidth: 56,
width: 56,
Cell: (props: { row: { original: ListItemType } }) => (
<ActionsCell projectId={projectId} {...props} />
),
disableSortBy: true,
},
],
[projectId, environments, onToggle]
[projectId, environments, onToggle, loading]
);
const initialState = useMemo(
@ -250,13 +244,15 @@ export const ProjectFeatureToggles = ({
);
const {
state: { filters },
getTableProps,
getTableBodyProps,
allColumns,
headerGroups,
rows,
state: { filters },
getTableBodyProps,
getTableProps,
prepareRow,
setFilter,
setHiddenColumns,
} = useTable(
{
columns: columns as any[], // TODO: fix after `react-table` v8 update
@ -275,7 +271,6 @@ export const ProjectFeatureToggles = ({
() => filters?.find(filterRow => filterRow?.id === 'name')?.value || '',
[filters]
);
return (
<PageContent
isLoading={loading}
@ -291,6 +286,13 @@ export const ProjectFeatureToggles = ({
initialValue={filter}
onChange={value => setFilter('name', value)}
/>
<ColumnsMenu
allColumns={allColumns}
staticColumns={['Actions', 'name']}
dividerAfter={['createdAt']}
dividerBefore={['Actions']}
setHiddenColumns={setHiddenColumns}
/>
<PageHeader.Divider />
<ResponsiveButton
onClick={() =>

View File

@ -41,7 +41,7 @@ export const useStyles = makeStyles()(theme => ({
},
},
subtitle: {
marginBottom: '1.25rem',
marginBottom: '1rem',
},
emphazisedText: {
fontSize: '1.5rem',

View File

@ -121,7 +121,7 @@ const ProjectInfo = ({
/>
</div>
<div className={styles.idContainer}>
<p data-loading>projectId: {id}</p>
<p>projectId: {id}</p>
</div>
</div>

View File

@ -13,10 +13,10 @@ exports[`renders correctly 1`] = `
className="tss-1ylehva-headerContainer"
>
<div
className="tss-1dw2af8-topContainer"
className="tss-1uxyh7x-topContainer"
>
<div
className=""
className="tss-sd6bs4-header"
data-loading={true}
>
<h1
@ -26,7 +26,7 @@ exports[`renders correctly 1`] = `
</h1>
</div>
<div
className="tss-1u1bjy8-headerActions"
className="tss-u5t8ea-headerActions"
>
<span
id="useId-0"
@ -66,7 +66,7 @@ exports[`renders correctly 1`] = `
</div>
</div>
<div
className="tss-f35ha2-bodyContainer"
className="tss-54jt3w-bodyContainer"
>
<ul
className="MuiList-root MuiList-padding mui-h4y409-MuiList-root"

View File

@ -13,10 +13,10 @@ exports[`renders an empty list correctly 1`] = `
className="tss-1ylehva-headerContainer"
>
<div
className="tss-1dw2af8-topContainer"
className="tss-1uxyh7x-topContainer"
>
<div
className=""
className="tss-sd6bs4-header"
data-loading={true}
>
<h1
@ -26,7 +26,7 @@ exports[`renders an empty list correctly 1`] = `
</h1>
</div>
<div
className="tss-1u1bjy8-headerActions"
className="tss-u5t8ea-headerActions"
>
<button
className="MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonBase-root mui-1aw3qf3-MuiButtonBase-root-MuiButton-root"
@ -54,7 +54,7 @@ exports[`renders an empty list correctly 1`] = `
</div>
</div>
<div
className="tss-f35ha2-bodyContainer"
className="tss-54jt3w-bodyContainer"
>
<ul
className="MuiList-root MuiList-padding mui-h4y409-MuiList-root"

View File

@ -7,9 +7,13 @@ type UsePersistentGlobalState<T> = () => [
setValue: React.Dispatch<React.SetStateAction<T>>
];
// Create a hook that stores global state (shared across all hook instances).
// The state is also persisted to localStorage and restored on page load.
// The localStorage state is not synced between tabs.
/**
* Create a hook that stores global state (shared across all hook instances).
* The state is also persisted to localStorage and restored on page load.
* The localStorage state is not synced between tabs.
*
* @deprecated
*/
export const createPersistentGlobalStateHook = <T extends object>(
key: string,
initialValue: T

View File

@ -4,6 +4,8 @@
* @see https://www.figma.com/file/qdwpPfuitJUNinm6mvmCmG/Unleash-application?node-id=7175%3A44590
*/
export const colors = {
white: '#FFFFFF',
black: '#000000',
grey: {
900: '#202021',
800: '#6E6E70',

View File

@ -234,5 +234,15 @@ export default createTheme({
},
},
},
MuiSwitch: {
styleOverrides: {
switchBase: {
zIndex: 1,
'&:not(.Mui-checked) .MuiTouchRipple-child': {
color: colors.grey['500'],
},
},
},
},
},
});

View File

@ -77,5 +77,10 @@ declare module '@mui/system/createTheme/shape' {
borderRadiusExtraLarge: string;
}
}
declare module '@mui/material/styles/zIndex' {
interface ZIndex {
sticky: number;
}
}
export {};