mirror of
https://github.com/Unleash/unleash.git
synced 2025-04-10 01:16:39 +02:00
Feature list table (#908)
* experiment with generic table * feat: example implementation of sortable table interfaces * add enhanced table header Co-authored-by: Nuno Góis <github@nunogois.com> * table cleanup Co-authored-by: Nuno Góis Co-authored-by: Fredrik Strand Oseberg * useSort hook interface surface Co-authored-by: Nuno Góis <github@nunogois.com> * sort handler initial implementation Co-authored-by: Tymoteusz Czech <Tymek@users.noreply.github.com> * new table unified components * feature flags table components Co-authored-by: Nuno Góis <github@nunogois.com> * feat: new table sort hook * feat: table sort * useSearch hook implementation * update new sort hook tests * sortable headers hook * feat: add sort to other table features * move experimental table hooks to a directory * update new table header styles * fix: header, tableActions * add some details like pagination and highlighter so we keep them in mind * feature table cells * update new table sort logic * new pagination * fix formatting and remove unused component * fix: adapt useSearch default search to text instead of regex (PR #924) * fix: update table title based on visible rows * fix: remove test route * refactor: move table experiment files * features table experimentation * feat: enhanced feature flags table * fix: features default sort * feat: enhanced table loading * fix: table theme after mui5 update * features list placeholder * add react-table * update snapshots after theme change * remove unused files * fix: improve features table after review * refactor: rename feature type cell variables Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com> Co-authored-by: Nuno Góis <github@nunogois.com> Co-authored-by: Tymoteusz Czech <Tymek@users.noreply.github.com>
This commit is contained in:
parent
d8143c6ff4
commit
1772997d28
@ -55,6 +55,8 @@
|
||||
"@types/node": "17.0.18",
|
||||
"@types/react": "17.0.44",
|
||||
"@types/react-dom": "17.0.16",
|
||||
"@types/react-router-dom": "5.3.3",
|
||||
"@types/react-table": "^7.7.11",
|
||||
"@types/react-test-renderer": "17.0.2",
|
||||
"@types/react-timeago": "4.1.3",
|
||||
"@types/semver": "^7.3.9",
|
||||
@ -82,6 +84,7 @@
|
||||
"react-hooks-global-state": "1.0.2",
|
||||
"react-router-dom": "6.3.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-table": "^7.7.0",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"react-timeago": "6.2.1",
|
||||
"sass": "1.51.0",
|
||||
|
@ -32,7 +32,7 @@ const UsersAdmin = () => {
|
||||
show={
|
||||
<div className={styles.tableActions}>
|
||||
<TableActions
|
||||
search={search}
|
||||
initialSearchValue={search}
|
||||
onSearch={search =>
|
||||
setSearch(search)
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
import { unleashGrey } from 'themes/themeColors';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
tableRow: {
|
||||
'& > td': {
|
||||
padding: '4px 16px',
|
||||
borderColor: unleashGrey[300],
|
||||
borderColor: theme.palette.grey[300],
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: unleashGrey[100],
|
||||
backgroundColor: theme.palette.grey[100],
|
||||
},
|
||||
},
|
||||
tableCellHeader: {
|
||||
'& > th': {
|
||||
backgroundColor: unleashGrey[200],
|
||||
backgroundColor: theme.palette.grey[200],
|
||||
fontWeight: 'normal',
|
||||
border: 0,
|
||||
'&:first-of-type': {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useFeaturesArchive } from 'hooks/api/getters/useFeaturesArchive/useFeaturesArchive';
|
||||
import { FeatureToggleList } from '../feature/FeatureToggleList/FeatureToggleList';
|
||||
import { FeatureToggleList } from '../feature/FeatureToggleList/FeatureToggleArchiveList';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||
import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FC } from 'react';
|
||||
import { useProjectFeaturesArchive } from 'hooks/api/getters/useProjectFeaturesArchive/useProjectFeaturesArchive';
|
||||
import { FeatureToggleList } from '../feature/FeatureToggleList/FeatureToggleList';
|
||||
import { FeatureToggleList } from '../feature/FeatureToggleList/FeatureToggleArchiveList';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||
import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi';
|
||||
|
@ -1,16 +1,26 @@
|
||||
import { VFC } from 'react';
|
||||
|
||||
interface IHighlighterProps {
|
||||
search: string;
|
||||
children: string;
|
||||
search?: string;
|
||||
children?: string;
|
||||
caseSensitive?: boolean;
|
||||
}
|
||||
|
||||
const escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
export const Highlighter = ({
|
||||
export const Highlighter: VFC<IHighlighterProps> = ({
|
||||
search,
|
||||
children,
|
||||
caseSensitive,
|
||||
}: IHighlighterProps) => {
|
||||
}) => {
|
||||
if (!children) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!search) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
const regex = new RegExp(escapeRegex(search), caseSensitive ? 'g' : 'gi');
|
||||
|
||||
return (
|
||||
|
@ -1,27 +0,0 @@
|
||||
import { ListItem } from '@mui/material';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from 'component/common/ListPlaceholder/ListPlaceholder.styles';
|
||||
|
||||
interface IListPlaceholderProps {
|
||||
text: string;
|
||||
link?: string;
|
||||
linkText?: string;
|
||||
}
|
||||
|
||||
const ListPlaceholder = ({ text, link, linkText }: IListPlaceholderProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
<ListItem className={styles.emptyStateListItem}>
|
||||
{text}
|
||||
<ConditionallyRender
|
||||
condition={Boolean(link && linkText)}
|
||||
// @ts-expect-error
|
||||
show={<Link to={link}>Add your first toggle</Link>}
|
||||
/>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListPlaceholder;
|
@ -18,6 +18,9 @@ interface IPaginateUIProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
const PaginateUI = ({
|
||||
pages,
|
||||
pageIndex,
|
||||
|
@ -0,0 +1,8 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
|
||||
const SearchHighlightContext = createContext('');
|
||||
|
||||
export const SearchHighlightProvider = SearchHighlightContext.Provider;
|
||||
|
||||
export const useSearchHighlightContext = () =>
|
||||
useContext(SearchHighlightContext);
|
@ -0,0 +1,27 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
tableCellHeaderSortable: {
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
},
|
||||
sortButton: {
|
||||
all: 'unset',
|
||||
padding: theme.spacing(2),
|
||||
fontWeight: theme.fontWeight.medium,
|
||||
width: '100%',
|
||||
'&:focus-visible, &:active': {
|
||||
outline: 'revert',
|
||||
},
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.grey[400],
|
||||
},
|
||||
boxSizing: 'inherit',
|
||||
cursor: 'pointer',
|
||||
},
|
||||
sorted: {
|
||||
fontWeight: theme.fontWeight.bold,
|
||||
},
|
||||
}));
|
@ -0,0 +1,67 @@
|
||||
import React, { FC, MouseEventHandler, useContext } from 'react';
|
||||
import { TableCell } from '@mui/material';
|
||||
import classnames from 'classnames';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './CellSortable.styles';
|
||||
import { AnnouncerContext } from 'component/common/Announcer/AnnouncerContext/AnnouncerContext';
|
||||
import { SortArrow } from './SortArrow/SortArrow';
|
||||
|
||||
interface ICellSortableProps {
|
||||
isSortable?: boolean;
|
||||
isSorted?: boolean;
|
||||
isDescending?: boolean;
|
||||
ariaTitle?: string;
|
||||
onClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
export const CellSortable: FC<ICellSortableProps> = ({
|
||||
children,
|
||||
isSortable = true,
|
||||
isSorted = false,
|
||||
isDescending,
|
||||
ariaTitle,
|
||||
onClick = () => {},
|
||||
}) => {
|
||||
const { setAnnouncement } = useContext(AnnouncerContext);
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
const ariaSort = isSorted
|
||||
? isDescending
|
||||
? 'descending'
|
||||
: 'ascending'
|
||||
: undefined;
|
||||
|
||||
const onSortClick: MouseEventHandler<HTMLButtonElement> = event => {
|
||||
onClick(event);
|
||||
setAnnouncement(
|
||||
`Sorted${ariaTitle ? ` by ${ariaTitle} ` : ''}, ${
|
||||
isDescending ? 'ascending' : 'descending'
|
||||
}`
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<TableCell
|
||||
component="th"
|
||||
aria-sort={ariaSort}
|
||||
className={classnames(styles.tableCellHeaderSortable)}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={isSortable}
|
||||
show={
|
||||
<button
|
||||
className={classnames(
|
||||
styles.sortButton,
|
||||
isSorted && styles.sorted
|
||||
)}
|
||||
onClick={onSortClick}
|
||||
>
|
||||
{children}
|
||||
<SortArrow isSorted={isSorted} isDesc={isDescending} />
|
||||
</button>
|
||||
}
|
||||
elseShow={children}
|
||||
/>
|
||||
</TableCell>
|
||||
);
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
icon: {
|
||||
marginLeft: theme.spacing(0.5),
|
||||
color: theme.palette.grey[700],
|
||||
fontSize: theme.fontSizes.mainHeader,
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
sorted: {
|
||||
color: theme.palette.grey[900],
|
||||
},
|
||||
}));
|
@ -0,0 +1,50 @@
|
||||
import { VFC } from 'react';
|
||||
import {
|
||||
KeyboardArrowDown,
|
||||
KeyboardArrowUp,
|
||||
UnfoldMoreOutlined,
|
||||
} from '@mui/icons-material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './SortArrow.styles';
|
||||
import classnames from 'classnames';
|
||||
|
||||
interface ISortArrowProps {
|
||||
isSorted?: boolean;
|
||||
isDesc?: boolean;
|
||||
}
|
||||
|
||||
export const SortArrow: VFC<ISortArrowProps> = ({
|
||||
isSorted: sorted,
|
||||
isDesc: desc = false,
|
||||
}) => {
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={Boolean(sorted)}
|
||||
show={
|
||||
<ConditionallyRender
|
||||
condition={Boolean(desc)}
|
||||
show={
|
||||
<KeyboardArrowDown
|
||||
className={classnames(styles.icon, styles.sorted)}
|
||||
fontSize="inherit"
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<KeyboardArrowUp
|
||||
className={classnames(styles.icon, styles.sorted)}
|
||||
fontSize="inherit"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<UnfoldMoreOutlined
|
||||
className={styles.icon}
|
||||
fontSize="inherit"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,21 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
tableHeader: {
|
||||
'& > th': {
|
||||
border: 0,
|
||||
'&:first-of-type': {
|
||||
borderTopLeftRadius: '8px',
|
||||
borderBottomLeftRadius: '8px',
|
||||
},
|
||||
'&:last-of-type': {
|
||||
borderTopRightRadius: '8px',
|
||||
borderBottomRightRadius: '8px',
|
||||
},
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
marginLeft: theme.spacing(0.5),
|
||||
fontSize: 18,
|
||||
},
|
||||
}));
|
@ -0,0 +1,47 @@
|
||||
import { VFC } from 'react';
|
||||
import { TableHead, TableRow } from '@mui/material';
|
||||
import { HeaderGroup } from 'react-table';
|
||||
import { useStyles } from './SortableTableHeader.styles';
|
||||
import { CellSortable } from './CellSortable/CellSortable';
|
||||
|
||||
interface ISortableTableHeaderProps {
|
||||
headerGroups: HeaderGroup<object>[];
|
||||
}
|
||||
|
||||
export const SortableTableHeader: VFC<ISortableTableHeaderProps> = ({
|
||||
headerGroups,
|
||||
}) => {
|
||||
const { classes: styles } = useStyles();
|
||||
return (
|
||||
<TableHead>
|
||||
{headerGroups.map(headerGroup => (
|
||||
<TableRow
|
||||
{...headerGroup.getHeaderGroupProps()}
|
||||
className={styles.tableHeader}
|
||||
>
|
||||
{headerGroup.headers.map(column => {
|
||||
const content = column.render('Header');
|
||||
|
||||
return (
|
||||
<CellSortable
|
||||
{...column.getHeaderProps(
|
||||
column.getSortByToggleProps()
|
||||
)}
|
||||
ariaTitle={
|
||||
typeof content === 'string'
|
||||
? content
|
||||
: undefined
|
||||
}
|
||||
isSortable={column.canSort}
|
||||
isSorted={column.isSorted}
|
||||
isDescending={column.isSortedDesc}
|
||||
>
|
||||
{content}
|
||||
</CellSortable>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHead>
|
||||
);
|
||||
};
|
@ -1,9 +1,18 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
import { unleashGrey } from 'themes/themeColors';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
tableActions: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
'&>button': {
|
||||
padding: theme.spacing(1),
|
||||
flexShrink: 0,
|
||||
},
|
||||
paddingRight: theme.spacing(1),
|
||||
},
|
||||
fieldWidth: {
|
||||
width: '49px',
|
||||
width: '45px',
|
||||
'& .search-icon': {
|
||||
marginRight: 0,
|
||||
},
|
||||
@ -19,7 +28,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
},
|
||||
fieldWidthEnter: {
|
||||
width: '100%',
|
||||
width: '250px',
|
||||
transition: 'width 0.6s',
|
||||
'& .search-icon': {
|
||||
marginRight: '8px',
|
||||
@ -37,7 +46,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
},
|
||||
fieldWidthLeave: {
|
||||
width: '49px',
|
||||
width: '45px',
|
||||
transition: 'width 0.6s',
|
||||
'& .search-icon': {
|
||||
marginRight: 0,
|
||||
@ -53,11 +62,11 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
verticalSeparator: {
|
||||
height: '100%',
|
||||
backgroundColor: unleashGrey[500],
|
||||
backgroundColor: theme.palette.grey[500],
|
||||
width: '1px',
|
||||
display: 'inline-block',
|
||||
marginLeft: '16px',
|
||||
marginRight: '32px',
|
||||
marginLeft: theme.spacing(2),
|
||||
marginRight: theme.spacing(4),
|
||||
padding: '10px 0',
|
||||
verticalAlign: 'middle',
|
||||
},
|
||||
|
@ -1,60 +1,87 @@
|
||||
import { useState } from 'react';
|
||||
import { FC, useState } from 'react';
|
||||
import { IconButton, Tooltip } from '@mui/material';
|
||||
import { Search } from '@mui/icons-material';
|
||||
import { useAsyncDebounce } from 'react-table';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import AnimateOnMount from 'component/common/AnimateOnMount/AnimateOnMount';
|
||||
import { TableSearchField } from 'component/common/Table/TableActions/TableSearchField/TableSearchField';
|
||||
import { useStyles } from 'component/common/Table/TableActions/TableActions.styles';
|
||||
import { TableSearchField } from './TableSearchField/TableSearchField';
|
||||
import { useStyles } from './TableActions.styles';
|
||||
|
||||
interface ITableActionsProps {
|
||||
search: string;
|
||||
onSearch: (value: string) => void;
|
||||
initialSearchValue?: string;
|
||||
onSearch?: (value: string) => void;
|
||||
searchTip?: string;
|
||||
isSeparated?: boolean;
|
||||
}
|
||||
|
||||
export const TableActions = ({ search, onSearch }: ITableActionsProps) => {
|
||||
const [searchExpanded, setSearchExpanded] = useState(false);
|
||||
export const TableActions: FC<ITableActionsProps> = ({
|
||||
initialSearchValue: search,
|
||||
onSearch = () => {},
|
||||
searchTip = 'Search',
|
||||
children,
|
||||
isSeparated,
|
||||
}) => {
|
||||
const [searchExpanded, setSearchExpanded] = useState(Boolean(search));
|
||||
const [searchInputState, setSearchInputState] = useState(search);
|
||||
const [animating, setAnimating] = useState(false);
|
||||
const debouncedOnSearch = useAsyncDebounce(onSearch, 200);
|
||||
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
const onBlur = (clear = false) => {
|
||||
if (!search || clear) {
|
||||
if (!searchInputState || clear) {
|
||||
setSearchExpanded(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onSearchChange = (value: string) => {
|
||||
debouncedOnSearch(value);
|
||||
setSearchInputState(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimateOnMount
|
||||
mounted={searchExpanded}
|
||||
start={styles.fieldWidth}
|
||||
enter={styles.fieldWidthEnter}
|
||||
leave={styles.fieldWidthLeave}
|
||||
onStart={() => setAnimating(true)}
|
||||
onEnd={() => setAnimating(false)}
|
||||
>
|
||||
<TableSearchField
|
||||
value={search}
|
||||
onChange={onSearch}
|
||||
placeholder="Search users..."
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
</AnimateOnMount>
|
||||
<div className={styles.tableActions}>
|
||||
<ConditionallyRender
|
||||
condition={!searchExpanded && !animating}
|
||||
condition={Boolean(onSearch)}
|
||||
show={
|
||||
<Tooltip title="Search users" arrow>
|
||||
<IconButton
|
||||
aria-label="Search users"
|
||||
onClick={() => setSearchExpanded(true)}
|
||||
size="large"
|
||||
<>
|
||||
<AnimateOnMount
|
||||
mounted={searchExpanded}
|
||||
start={styles.fieldWidth}
|
||||
enter={styles.fieldWidthEnter}
|
||||
leave={styles.fieldWidthLeave}
|
||||
onStart={() => setAnimating(true)}
|
||||
onEnd={() => setAnimating(false)}
|
||||
>
|
||||
<Search />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<TableSearchField
|
||||
value={searchInputState!}
|
||||
onChange={onSearchChange}
|
||||
placeholder={`${searchTip}...`}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
</AnimateOnMount>
|
||||
<ConditionallyRender
|
||||
condition={!searchExpanded && !animating}
|
||||
show={
|
||||
<Tooltip title={searchTip} arrow>
|
||||
<IconButton
|
||||
aria-label={searchTip}
|
||||
onClick={() => setSearchExpanded(true)}
|
||||
size="large"
|
||||
>
|
||||
<Search />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<div className={styles.verticalSeparator} />
|
||||
</>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(isSeparated)}
|
||||
show={<div className={styles.verticalSeparator} />}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { IconButton, InputBase, Tooltip } from '@mui/material';
|
||||
import { Search, Close } from '@mui/icons-material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from 'component/common/Table/TableActions/TableSearchField/TableSearchField.styles';
|
||||
import classnames from 'classnames';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './TableSearchField.styles';
|
||||
|
||||
interface ITableSearchFieldProps {
|
||||
value: string;
|
||||
value?: string;
|
||||
onChange: (value: string) => void;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
@ -13,7 +13,7 @@ interface ITableSearchFieldProps {
|
||||
}
|
||||
|
||||
export const TableSearchField = ({
|
||||
value,
|
||||
value = '',
|
||||
onChange,
|
||||
className,
|
||||
placeholder,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
import { unleashGrey } from 'themes/themeColors';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
tableCellHeaderSortable: {
|
||||
@ -9,13 +8,13 @@ export const useStyles = makeStyles()(theme => ({
|
||||
'& > svg': {
|
||||
fontSize: 18,
|
||||
verticalAlign: 'middle',
|
||||
color: unleashGrey[700],
|
||||
color: theme.palette.grey[700],
|
||||
marginLeft: '4px',
|
||||
},
|
||||
'&.sorted': {
|
||||
fontWeight: 'bold',
|
||||
'& > svg': {
|
||||
color: unleashGrey[900],
|
||||
color: theme.palette.grey[900],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -29,9 +28,9 @@ export const useStyles = makeStyles()(theme => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'&:hover': {
|
||||
backgroundColor: unleashGrey[400],
|
||||
backgroundColor: theme.palette.grey[400],
|
||||
'& > svg': {
|
||||
color: unleashGrey[900],
|
||||
color: theme.palette.grey[900],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -23,6 +23,9 @@ interface ITableCellSortableProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated No longer in use. See `SortableTableHeader`. Remove when Users table is refactored.
|
||||
*/
|
||||
export const TableCellSortable = ({
|
||||
className,
|
||||
name,
|
||||
@ -40,12 +43,6 @@ export const TableCellSortable = ({
|
||||
: 'ascending'
|
||||
: undefined;
|
||||
|
||||
const cellClassName = classnames(
|
||||
className,
|
||||
styles.tableCellHeaderSortable,
|
||||
sort.type === name && 'sorted'
|
||||
);
|
||||
|
||||
const onSortClick = () => {
|
||||
setSort(prev => ({
|
||||
desc: !Boolean(prev.desc),
|
||||
@ -57,7 +54,14 @@ export const TableCellSortable = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<TableCell aria-sort={ariaSort} className={cellClassName}>
|
||||
<TableCell
|
||||
aria-sort={ariaSort}
|
||||
className={classnames(
|
||||
className,
|
||||
styles.tableCellHeaderSortable,
|
||||
sort.type === name && 'sorted'
|
||||
)}
|
||||
>
|
||||
<button className={styles.sortButton} onClick={onSortClick}>
|
||||
{children}
|
||||
<ConditionallyRender
|
||||
|
@ -0,0 +1,14 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
panel: {
|
||||
width: '100%',
|
||||
marginBottom: theme.spacing(2),
|
||||
borderRadius: theme.spacing(1.5),
|
||||
paddingBottom: theme.spacing(4),
|
||||
},
|
||||
content: {
|
||||
padding: theme.spacing(4),
|
||||
paddingBottom: 0,
|
||||
},
|
||||
}));
|
@ -0,0 +1,18 @@
|
||||
import { forwardRef, ReactNode } from 'react';
|
||||
import { Paper } from '@mui/material';
|
||||
import { Box } from '@mui/material';
|
||||
import { useStyles } from './TablePanel.styles';
|
||||
|
||||
export const TablePanel = forwardRef<
|
||||
HTMLDivElement,
|
||||
{ children: ReactNode; header?: ReactNode }
|
||||
>(({ header, children, ...props }, ref) => {
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Paper ref={ref} className={styles.panel} {...props}>
|
||||
{header}
|
||||
<Box className={styles.content}>{children}</Box>
|
||||
</Paper>
|
||||
);
|
||||
});
|
@ -8,5 +8,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(4),
|
||||
},
|
||||
}));
|
@ -0,0 +1,11 @@
|
||||
import { FC } from 'react';
|
||||
import { Box } from '@mui/material';
|
||||
import { useStyles } from 'component/common/Table/TablePlaceholder/TablePlaceholder.styles';
|
||||
|
||||
const TablePlaceholder: FC = ({ children }) => {
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return <Box className={styles.emptyStateListItem}>{children}</Box>;
|
||||
};
|
||||
|
||||
export default TablePlaceholder;
|
@ -0,0 +1,12 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
root: {
|
||||
paddingLeft: theme.spacing(4),
|
||||
paddingRight: theme.spacing(4),
|
||||
paddingTop: theme.spacing(2.5),
|
||||
paddingBottom: theme.spacing(2.5),
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
}));
|
@ -0,0 +1,20 @@
|
||||
import { FC } from 'react';
|
||||
import { Toolbar, Typography } from '@mui/material';
|
||||
import { useStyles } from './TableToolbar.styles';
|
||||
|
||||
interface ITableToolbarProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export const TableToolbar: FC<ITableToolbarProps> = ({ title, children }) => {
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Toolbar className={styles.root}>
|
||||
<Typography variant="h1" component="h1" data-loading>
|
||||
{title}
|
||||
</Typography>
|
||||
{children}
|
||||
</Toolbar>
|
||||
);
|
||||
};
|
@ -9,12 +9,10 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
||||
import PageContent from 'component/common/PageContent/PageContent';
|
||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||
import AccessContext from 'contexts/AccessContext';
|
||||
import ListPlaceholder from 'component/common/ListPlaceholder/ListPlaceholder';
|
||||
import { IFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||
import { FeatureToggleListItem } from './FeatureToggleListItem/FeatureToggleListItem';
|
||||
import { FeatureToggleListActions } from './FeatureToggleListActions/FeatureToggleListActions';
|
||||
import { CreateFeatureButton } from '../CreateFeatureButton/CreateFeatureButton';
|
||||
import { useCreateFeaturePath } from '../CreateFeatureButton/useCreateFeaturePath';
|
||||
import { IFeaturesSort } from 'hooks/useFeaturesSort';
|
||||
import { FeatureSchema } from 'openapi';
|
||||
import { useStyles } from './styles';
|
||||
@ -63,7 +61,6 @@ export const FeatureToggleList: VFC<IFeatureToggleListProps> = ({
|
||||
setSort,
|
||||
}) => {
|
||||
const { hasAccess } = useContext(AccessContext);
|
||||
const createFeature = useCreateFeaturePath(filter);
|
||||
const { classes: styles } = useStyles();
|
||||
const smallScreen = useMediaQuery('(max-width:800px)');
|
||||
const mobileView = useMediaQuery('(max-width:600px)');
|
||||
@ -101,26 +98,9 @@ export const FeatureToggleList: VFC<IFeatureToggleListProps> = ({
|
||||
/>
|
||||
))}
|
||||
elseShow={
|
||||
<ConditionallyRender
|
||||
condition={Boolean(isArchive)}
|
||||
show={
|
||||
<ListItem className={styles.emptyStateListItem}>
|
||||
No archived features.
|
||||
</ListItem>
|
||||
}
|
||||
elseShow={
|
||||
<ConditionallyRender
|
||||
condition={Boolean(createFeature?.access)}
|
||||
show={() => (
|
||||
<ListPlaceholder
|
||||
text="No features available. Get started by adding a new feature toggle."
|
||||
link={createFeature?.path}
|
||||
linkText="Add your first toggle"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ListItem className={styles.emptyStateListItem}>
|
||||
No archived features.
|
||||
</ListItem>
|
||||
}
|
||||
/>
|
||||
);
|
@ -1,24 +1,22 @@
|
||||
import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||
import { FeatureToggleList } from './FeatureToggleList';
|
||||
import { useFeaturesSort } from 'hooks/useFeaturesSort';
|
||||
import { FeatureSchema } from 'openapi';
|
||||
import { FeatureToggleListTable } from './FeatureToggleListTable/FeatureToggleListTable';
|
||||
|
||||
const featuresPlaceholder: FeatureSchema[] = Array(7).fill({
|
||||
name: 'Name of the feature',
|
||||
description: 'Short description of the feature',
|
||||
type: '-',
|
||||
createdAt: new Date(2022, 1, 1),
|
||||
project: 'projectID',
|
||||
});
|
||||
|
||||
export const FeatureToggleListContainer = () => {
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { features = [], loading } = useFeatures();
|
||||
const { filtered, filter, setFilter } = useFeaturesFilter(features);
|
||||
const { sorted, sort, setSort } = useFeaturesSort(filtered);
|
||||
|
||||
return (
|
||||
<FeatureToggleList
|
||||
features={sorted}
|
||||
loading={loading}
|
||||
flags={uiConfig.flags}
|
||||
filter={filter}
|
||||
setFilter={setFilter}
|
||||
sort={sort}
|
||||
setSort={setSort}
|
||||
<FeatureToggleListTable
|
||||
data={loading ? featuresPlaceholder : features}
|
||||
isLoading={loading}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -27,6 +27,9 @@ interface IFeatureToggleListItemProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export const FeatureToggleListItem = memo<IFeatureToggleListItemProps>(
|
||||
({
|
||||
feature,
|
||||
|
@ -0,0 +1,32 @@
|
||||
import { VFC } from 'react';
|
||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||
import { formatDateYMD, formatDateYMDHMS } from 'utils/formatDate';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { Tooltip } from '@mui/material';
|
||||
|
||||
interface IDateCellProps {
|
||||
value?: Date | null;
|
||||
}
|
||||
|
||||
export const DateCell: VFC<IDateCellProps> = ({ value }) => {
|
||||
const { locationSettings } = useLocationSettings();
|
||||
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={Boolean(value)}
|
||||
show={
|
||||
<Tooltip
|
||||
title={formatDateYMDHMS(
|
||||
value as Date,
|
||||
locationSettings.locale
|
||||
)}
|
||||
arrow
|
||||
>
|
||||
<span data-loading>
|
||||
{formatDateYMD(value as Date, locationSettings.locale)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,13 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
description: {
|
||||
color: theme.palette.grey[800],
|
||||
fontSize: 'inherit',
|
||||
display: 'inline-block',
|
||||
maxWidth: '250px',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
},
|
||||
}));
|
@ -0,0 +1,53 @@
|
||||
import { FC } from 'react';
|
||||
import { Link, Typography } from '@mui/material';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './FeatureNameCell.styles';
|
||||
import { Highlighter } from 'component/common/Highlighter/Highlighter';
|
||||
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
|
||||
interface IFeatureNameCellProps {
|
||||
name?: string;
|
||||
project?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export const FeatureNameCell: FC<IFeatureNameCellProps> = ({
|
||||
name,
|
||||
project,
|
||||
description,
|
||||
}) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const search = useSearchHighlightContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to={`/projects/${project}/features/${name}`}
|
||||
underline="hover"
|
||||
data-loading
|
||||
>
|
||||
<Highlighter search={search}>{name}</Highlighter>
|
||||
</Link>
|
||||
|
||||
<ConditionallyRender
|
||||
condition={Boolean(description)}
|
||||
show={
|
||||
<>
|
||||
<br />
|
||||
<Typography
|
||||
className={styles.description}
|
||||
component="span"
|
||||
data-loading
|
||||
>
|
||||
<Highlighter search={search}>
|
||||
{description}
|
||||
</Highlighter>
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
container: {
|
||||
width: '38px',
|
||||
height: '38px',
|
||||
background: 'gray',
|
||||
borderRadius: '4px',
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: theme.fontSizes.smallerBody,
|
||||
margin: '0 auto',
|
||||
},
|
||||
}));
|
@ -0,0 +1,115 @@
|
||||
import React, { FC, VFC } from 'react';
|
||||
import TimeAgo from 'react-timeago';
|
||||
import { Tooltip, useTheme } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { useStyles } from './FeatureSeenCell.styles';
|
||||
|
||||
function shortenUnitName(unit?: string): string {
|
||||
switch (unit) {
|
||||
case 'second':
|
||||
return 's';
|
||||
case 'minute':
|
||||
return 'm';
|
||||
case 'hour':
|
||||
return 'h';
|
||||
case 'day':
|
||||
return 'D';
|
||||
case 'week':
|
||||
return 'W';
|
||||
case 'month':
|
||||
return 'M';
|
||||
case 'year':
|
||||
return 'Y';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
const useFeatureColor = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (unit?: string): string => {
|
||||
switch (unit) {
|
||||
case 'second':
|
||||
return theme.palette.success.light;
|
||||
case 'minute':
|
||||
return theme.palette.success.light;
|
||||
case 'hour':
|
||||
return theme.palette.success.light;
|
||||
case 'day':
|
||||
return theme.palette.success.light;
|
||||
case 'week':
|
||||
return theme.palette.warning.light;
|
||||
case 'month':
|
||||
return theme.palette.error.light;
|
||||
case 'year':
|
||||
return theme.palette.error.light;
|
||||
default:
|
||||
return theme.palette.grey[100];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
interface IFeatureSeenCellProps {
|
||||
value?: string | Date | null;
|
||||
}
|
||||
|
||||
const Wrapper: FC<{ unit?: string; tooltip: string }> = ({
|
||||
unit,
|
||||
tooltip,
|
||||
children,
|
||||
}) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const getColor = useFeatureColor();
|
||||
|
||||
return (
|
||||
<Tooltip title={tooltip} arrow>
|
||||
<div
|
||||
className={styles.container}
|
||||
style={{ background: getColor(unit) }}
|
||||
data-loading
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
export const FeatureSeenCell: VFC<IFeatureSeenCellProps> = ({
|
||||
value: lastSeenAt,
|
||||
}) => {
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={Boolean(lastSeenAt)}
|
||||
show={
|
||||
<TimeAgo
|
||||
date={lastSeenAt!}
|
||||
title=""
|
||||
live={false}
|
||||
formatter={(
|
||||
value: number,
|
||||
unit: string,
|
||||
suffix: string
|
||||
) => {
|
||||
return (
|
||||
<Wrapper
|
||||
tooltip={`Last usage reported ${value} ${unit}${
|
||||
value !== 1 ? 's' : ''
|
||||
} ${suffix}`}
|
||||
unit={unit}
|
||||
>
|
||||
{value}
|
||||
{shortenUnitName(unit)}
|
||||
</Wrapper>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<Wrapper tooltip="No usage reported from connected applications">
|
||||
–
|
||||
</Wrapper>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
@ -0,0 +1,11 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
status: {
|
||||
color: theme.palette.success.dark,
|
||||
fontSize: 'inherit',
|
||||
},
|
||||
stale: {
|
||||
color: theme.palette.error.dark,
|
||||
},
|
||||
}));
|
@ -0,0 +1,21 @@
|
||||
import { VFC } from 'react';
|
||||
import { Typography } from '@mui/material';
|
||||
import { useStyles } from './FeatureStaleCell.styles';
|
||||
import classnames from 'classnames';
|
||||
|
||||
interface IFeatureStaleCellProps {
|
||||
value?: boolean;
|
||||
}
|
||||
|
||||
export const FeatureStaleCell: VFC<IFeatureStaleCellProps> = ({ value }) => {
|
||||
const { classes: styles } = useStyles();
|
||||
return (
|
||||
<Typography
|
||||
component="span"
|
||||
className={classnames(styles.status, value && styles.stale)}
|
||||
data-loading
|
||||
>
|
||||
{value ? 'Stale' : 'Active'}
|
||||
</Typography>
|
||||
);
|
||||
};
|
@ -0,0 +1,193 @@
|
||||
import { useEffect, useMemo, VFC } from 'react';
|
||||
import {
|
||||
Link,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from '@mui/material';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { useGlobalFilter, useSortBy, useTable } from 'react-table';
|
||||
import useLoading from 'hooks/useLoading';
|
||||
import { SortableTableHeader } from 'component/common/Table/SortableTableHeader/SortableTableHeader';
|
||||
import { TableActions } from 'component/common/Table/TableActions/TableActions';
|
||||
import { TablePanel } from 'component/common/Table/TablePanel/TablePanel';
|
||||
import { TableToolbar } from 'component/common/Table/TableToolbar/TableToolbar';
|
||||
import TablePlaceholder from 'component/common/Table/TablePlaceholder/TablePlaceholder';
|
||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
import { DateCell } from './DateCell/DateCell';
|
||||
import { FeatureNameCell } from './FeatureNameCell/FeatureNameCell';
|
||||
import { FeatureSeenCell } from './FeatureSeenCell/FeatureSeenCell';
|
||||
import { FeatureStaleCell } from './FeatureStaleCell/FeatureStaleCell';
|
||||
import { FeatureTypeCell } from './FeatureTypeCell/FeatureTypeCell';
|
||||
import { LinkCell } from './LinkCell/LinkCell';
|
||||
import { CreateFeatureButton } from '../../CreateFeatureButton/CreateFeatureButton';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface IExperimentProps {
|
||||
data: Record<string, any>[];
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const sortTypes = {
|
||||
date: (a: any, b: any, id: string) =>
|
||||
b?.values?.[id]?.getTime() - a?.values?.[id]?.getTime(),
|
||||
boolean: (v1: any, v2: any, id: string) => {
|
||||
const a = v1?.values?.[id];
|
||||
const b = v2?.values?.[id];
|
||||
return a === b ? 0 : a ? 1 : -1;
|
||||
},
|
||||
alphanumeric: (a: any, b: any, id: string) =>
|
||||
a?.values?.[id]
|
||||
?.toLowerCase()
|
||||
.localeCompare(b?.values?.[id]?.toLowerCase()),
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
Header: 'Seen',
|
||||
accessor: 'lastSeenAt',
|
||||
Cell: FeatureSeenCell,
|
||||
sortType: 'date',
|
||||
},
|
||||
{
|
||||
Header: 'Type',
|
||||
accessor: 'type',
|
||||
Cell: FeatureTypeCell,
|
||||
},
|
||||
{
|
||||
Header: 'Feature toggle name',
|
||||
accessor: 'name',
|
||||
// @ts-expect-error // TODO: props type
|
||||
Cell: ({ row: { original } }) => <FeatureNameCell {...original} />,
|
||||
sortType: 'alphanumeric',
|
||||
},
|
||||
{
|
||||
Header: 'Created on',
|
||||
accessor: 'createdAt',
|
||||
Cell: DateCell,
|
||||
sortType: 'date',
|
||||
},
|
||||
{
|
||||
Header: 'Project ID',
|
||||
accessor: 'project',
|
||||
Cell: ({ value }: { value: string }) => (
|
||||
<LinkCell to={`/projects/${value}`}>{value}</LinkCell>
|
||||
),
|
||||
sortType: 'alphanumeric',
|
||||
},
|
||||
{
|
||||
Header: 'State',
|
||||
accessor: 'stale',
|
||||
Cell: FeatureStaleCell,
|
||||
sortType: 'boolean',
|
||||
},
|
||||
// Always hidden -- for search
|
||||
{
|
||||
accessor: 'description',
|
||||
},
|
||||
];
|
||||
|
||||
export const FeatureToggleListTable: VFC<IExperimentProps> = ({
|
||||
data,
|
||||
isLoading = false,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const isMediumScreen = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
const ref = useLoading(isLoading);
|
||||
|
||||
const initialState = useMemo(
|
||||
() => ({
|
||||
sortBy: [{ id: 'createdAt', desc: false }],
|
||||
hiddenColumns: ['description'],
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const {
|
||||
getTableProps,
|
||||
getTableBodyProps,
|
||||
headerGroups,
|
||||
rows,
|
||||
prepareRow,
|
||||
state: { globalFilter },
|
||||
setGlobalFilter,
|
||||
setHiddenColumns,
|
||||
} = useTable(
|
||||
{
|
||||
columns,
|
||||
data,
|
||||
initialState,
|
||||
sortTypes,
|
||||
autoResetGlobalFilter: false,
|
||||
},
|
||||
useGlobalFilter,
|
||||
useSortBy
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSmallScreen) {
|
||||
setHiddenColumns(['lastSeenAt', 'type', 'stale', 'description']);
|
||||
} else if (isMediumScreen) {
|
||||
setHiddenColumns(['lastSeenAt', 'stale', 'description']);
|
||||
} else {
|
||||
setHiddenColumns(['description']);
|
||||
}
|
||||
}, [setHiddenColumns, isSmallScreen, isMediumScreen]);
|
||||
|
||||
return (
|
||||
<TablePanel
|
||||
ref={ref}
|
||||
header={
|
||||
<TableToolbar title={`Feature Flags (${data.length})`}>
|
||||
<TableActions isSeparated onSearch={setGlobalFilter}>
|
||||
<Link
|
||||
component={RouterLink}
|
||||
to="/archive"
|
||||
underline="always"
|
||||
sx={{ marginRight: 3 }}
|
||||
>
|
||||
View archive
|
||||
</Link>
|
||||
<CreateFeatureButton
|
||||
loading={false}
|
||||
filter={{ query: '', project: 'default' }}
|
||||
/>
|
||||
</TableActions>
|
||||
</TableToolbar>
|
||||
}
|
||||
>
|
||||
<SearchHighlightProvider value={globalFilter}>
|
||||
<Table {...getTableProps()}>
|
||||
<SortableTableHeader headerGroups={headerGroups} />
|
||||
<TableBody {...getTableBodyProps()}>
|
||||
{rows.map(row => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<TableRow {...row.getRowProps()}>
|
||||
{row.cells.map(cell => (
|
||||
<TableCell {...cell.getCellProps()}>
|
||||
{cell.render('Cell')}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</SearchHighlightProvider>
|
||||
<ConditionallyRender
|
||||
condition={rows.length === 0}
|
||||
show={
|
||||
<TablePlaceholder>
|
||||
No features available. Get started by adding a new
|
||||
feature toggle.
|
||||
</TablePlaceholder>
|
||||
}
|
||||
/>
|
||||
</TablePanel>
|
||||
);
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
import { makeStyles } from 'tss-react/mui';
|
||||
|
||||
export const useStyles = makeStyles()(theme => ({
|
||||
icon: {
|
||||
marginTop: theme.spacing(0.5),
|
||||
color: theme.palette.grey[600],
|
||||
},
|
||||
}));
|
@ -0,0 +1,27 @@
|
||||
import { VFC } from 'react';
|
||||
import { Tooltip } from '@mui/material';
|
||||
import { getFeatureTypeIcons } from 'utils/getFeatureTypeIcons';
|
||||
import useFeatureTypes from 'hooks/api/getters/useFeatureTypes/useFeatureTypes';
|
||||
import { useStyles } from './FeatureTypeCell.styles';
|
||||
|
||||
interface IFeatureTypeProps {
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export const FeatureTypeCell: VFC<IFeatureTypeProps> = ({ value }) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const { featureTypes } = useFeatureTypes();
|
||||
const IconComponent = getFeatureTypeIcons(value);
|
||||
|
||||
const typeName = featureTypes
|
||||
.filter(type => type.id === value)
|
||||
.map(type => type.name);
|
||||
|
||||
const title = `This is a "${typeName || value}" toggle`;
|
||||
|
||||
return (
|
||||
<Tooltip arrow placement="right" title={title}>
|
||||
<IconComponent data-loading className={styles.icon} />
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
@ -0,0 +1,20 @@
|
||||
import { VFC } from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { Link } from '@mui/material';
|
||||
import { Highlighter } from 'component/common/Highlighter/Highlighter';
|
||||
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
|
||||
interface ILinkCellProps {
|
||||
to: string;
|
||||
children?: string;
|
||||
}
|
||||
|
||||
export const LinkCell: VFC<ILinkCellProps> = ({ children, to }) => {
|
||||
const search = useSearchHighlightContext();
|
||||
|
||||
return (
|
||||
<Link component={RouterLink} to={to} data-loading underline="hover">
|
||||
<Highlighter search={search}>{children}</Highlighter>
|
||||
</Link>
|
||||
);
|
||||
};
|
@ -76,6 +76,9 @@ const useFeatureToggLeProjectSort = createGlobalStateHook<ISortedState>(
|
||||
{ field: 'name', type: 'string', direction: 0 }
|
||||
);
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
const FeatureToggleListNew = ({
|
||||
features,
|
||||
loading,
|
||||
|
@ -7,7 +7,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
class="tss-fdcp7c-container"
|
||||
>
|
||||
<h1
|
||||
class="tss-16bfuiy-title"
|
||||
class="tss-1a5bydb-title"
|
||||
>
|
||||
Please help us improve
|
||||
</h1>
|
||||
@ -24,12 +24,12 @@ exports[`FeedbackCESForm 1`] = `
|
||||
class="tss-io6e1g-scoreInput"
|
||||
>
|
||||
<span
|
||||
class="tss-lys30y-scoreHelp"
|
||||
class="tss-b4a690-scoreHelp"
|
||||
>
|
||||
Very difficult
|
||||
</span>
|
||||
<label
|
||||
class="tss-mulaxt-scoreValue"
|
||||
class="tss-pq8zjr-scoreValue"
|
||||
>
|
||||
<input
|
||||
name="score"
|
||||
@ -41,7 +41,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="tss-mulaxt-scoreValue"
|
||||
class="tss-pq8zjr-scoreValue"
|
||||
>
|
||||
<input
|
||||
name="score"
|
||||
@ -53,7 +53,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="tss-mulaxt-scoreValue"
|
||||
class="tss-pq8zjr-scoreValue"
|
||||
>
|
||||
<input
|
||||
name="score"
|
||||
@ -65,7 +65,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="tss-mulaxt-scoreValue"
|
||||
class="tss-pq8zjr-scoreValue"
|
||||
>
|
||||
<input
|
||||
name="score"
|
||||
@ -77,7 +77,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="tss-mulaxt-scoreValue"
|
||||
class="tss-pq8zjr-scoreValue"
|
||||
>
|
||||
<input
|
||||
name="score"
|
||||
@ -89,7 +89,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="tss-mulaxt-scoreValue"
|
||||
class="tss-pq8zjr-scoreValue"
|
||||
>
|
||||
<input
|
||||
name="score"
|
||||
@ -101,7 +101,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
</span>
|
||||
</label>
|
||||
<label
|
||||
class="tss-mulaxt-scoreValue"
|
||||
class="tss-pq8zjr-scoreValue"
|
||||
>
|
||||
<input
|
||||
name="score"
|
||||
@ -113,7 +113,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
</span>
|
||||
</label>
|
||||
<span
|
||||
class="tss-lys30y-scoreHelp"
|
||||
class="tss-b4a690-scoreHelp"
|
||||
>
|
||||
Very easy
|
||||
</span>
|
||||
@ -131,7 +131,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
class="MuiFormControl-root MuiFormControl-fullWidth MuiTextField-root mui-wb57ya-MuiFormControl-root-MuiTextField-root"
|
||||
>
|
||||
<div
|
||||
class="MuiOutlinedInput-root MuiInputBase-root MuiInputBase-colorPrimary MuiInputBase-fullWidth MuiInputBase-formControl MuiInputBase-multiline mui-opjsp0-MuiInputBase-root-MuiOutlinedInput-root"
|
||||
class="MuiOutlinedInput-root MuiInputBase-root MuiInputBase-colorPrimary MuiInputBase-fullWidth MuiInputBase-formControl MuiInputBase-multiline mui-1rae96v-MuiInputBase-root-MuiOutlinedInput-root"
|
||||
>
|
||||
<textarea
|
||||
aria-invalid="false"
|
||||
@ -169,7 +169,7 @@ exports[`FeedbackCESForm 1`] = `
|
||||
hidden=""
|
||||
>
|
||||
<button
|
||||
class="MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonBase-root Mui-disabled tss-1t8daf-button mui-16l97z4-MuiButtonBase-root-MuiButton-root"
|
||||
class="MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonBase-root Mui-disabled tss-1t8daf-button mui-1aw3qf3-MuiButtonBase-root-MuiButton-root"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="submit"
|
||||
|
@ -52,7 +52,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||
>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -71,7 +71,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -90,7 +90,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -110,7 +110,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</li>
|
||||
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -130,7 +130,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</li>
|
||||
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -149,7 +149,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -168,7 +168,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -187,7 +187,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -223,7 +223,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||
>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -242,7 +242,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -261,7 +261,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -280,7 +280,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -299,7 +299,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -333,7 +333,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||
>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -352,7 +352,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -371,7 +371,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -390,7 +390,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -409,7 +409,7 @@ exports[`should render DrawerMenu 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -488,7 +488,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||
>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -507,7 +507,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -526,7 +526,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -546,7 +546,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</li>
|
||||
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -566,7 +566,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</li>
|
||||
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -585,7 +585,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -604,7 +604,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -623,7 +623,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -659,7 +659,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||
>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -678,7 +678,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -697,7 +697,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -716,7 +716,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -735,7 +735,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -769,7 +769,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||
>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -788,7 +788,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -807,7 +807,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -826,7 +826,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
@ -845,7 +845,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-1vgy6yu-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-dense MuiListItem-gutters MuiListItem-padding tss-1xj02bu-listItem mui-w8p5u6-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
|
@ -12,7 +12,7 @@ Array [
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="tss-16iyfoa-headerContainer"
|
||||
className="tss-8bhpw1-headerContainer"
|
||||
>
|
||||
<div
|
||||
className="tss-14s7qul-headerTitleContainer"
|
||||
@ -22,7 +22,7 @@ Array [
|
||||
data-loading={true}
|
||||
>
|
||||
<h1
|
||||
className="MuiTypography-root MuiTypography-h1 tss-whbfmi-headerTitle mui-1inkmva-MuiTypography-root"
|
||||
className="MuiTypography-root MuiTypography-h1 tss-whbfmi-headerTitle mui-ylrecv-MuiTypography-root"
|
||||
>
|
||||
Strategies
|
||||
</h1>
|
||||
@ -35,7 +35,7 @@ Array [
|
||||
>
|
||||
<button
|
||||
aria-describedby="useId-0"
|
||||
className="MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonBase-root mui-16l97z4-MuiButtonBase-root-MuiButton-root"
|
||||
className="MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonBase-root mui-1aw3qf3-MuiButtonBase-root-MuiButton-root"
|
||||
data-testid="ADD_NEW_STRATEGY_ID"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
@ -73,7 +73,7 @@ Array [
|
||||
className="MuiList-root MuiList-padding mui-h4y409-MuiList-root"
|
||||
>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-gutters MuiListItem-padding tss-18csf3q-listItem mui-uz8enf-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-gutters MuiListItem-padding tss-ynyzms-listItem mui-vlytkl-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
<div
|
||||
|
@ -11,7 +11,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="tss-16iyfoa-headerContainer"
|
||||
className="tss-8bhpw1-headerContainer"
|
||||
>
|
||||
<div
|
||||
className="tss-14s7qul-headerTitleContainer"
|
||||
@ -21,7 +21,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-loading={true}
|
||||
>
|
||||
<h1
|
||||
className="MuiTypography-root MuiTypography-h1 tss-whbfmi-headerTitle mui-1inkmva-MuiTypography-root"
|
||||
className="MuiTypography-root MuiTypography-h1 tss-whbfmi-headerTitle mui-ylrecv-MuiTypography-root"
|
||||
>
|
||||
Tag Types
|
||||
</h1>
|
||||
@ -30,7 +30,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
className="tss-ap2nhp-headerActions"
|
||||
>
|
||||
<button
|
||||
className="MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonBase-root mui-16l97z4-MuiButtonBase-root-MuiButton-root"
|
||||
className="MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButtonBase-root mui-1aw3qf3-MuiButtonBase-root-MuiButton-root"
|
||||
disabled={false}
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
@ -60,7 +60,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
className="MuiList-root MuiList-padding mui-h4y409-MuiList-root"
|
||||
>
|
||||
<li
|
||||
className="MuiListItem-root MuiListItem-gutters MuiListItem-padding mui-uz8enf-MuiListItem-root"
|
||||
className="MuiListItem-root MuiListItem-gutters MuiListItem-padding mui-vlytkl-MuiListItem-root"
|
||||
disabled={false}
|
||||
>
|
||||
No entries
|
||||
|
@ -1,6 +1,9 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { paginate } from 'utils/paginate';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
const usePagination = <T>(
|
||||
data: T[],
|
||||
limit: number,
|
||||
|
@ -15,6 +15,9 @@ import {
|
||||
import { ReportingSortType } from 'component/Reporting/constants';
|
||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
const useSort = () => {
|
||||
const [sortData, setSortData] = useState<{
|
||||
sortKey: ReportingSortType;
|
||||
|
@ -38,6 +38,7 @@ button {
|
||||
z-index: 9999;
|
||||
box-shadow: none;
|
||||
fill: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.skeleton::before {
|
||||
|
77
frontend/src/themes/colors.ts
Normal file
77
frontend/src/themes/colors.ts
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* IMPORTANT: Use full color palette ONLY in theme. Don't import colors directly from this file.
|
||||
*
|
||||
* @see https://www.figma.com/file/qdwpPfuitJUNinm6mvmCmG/Unleash-application?node-id=7175%3A44590
|
||||
*/
|
||||
export const colors = {
|
||||
grey: {
|
||||
900: '#202021',
|
||||
800: '#6E6E70',
|
||||
700: '#78787A',
|
||||
600: '#9F9FA1',
|
||||
500: '#BDBDBF',
|
||||
400: '#E1E1E3',
|
||||
300: '#EAEAED',
|
||||
200: '#F2F2F5',
|
||||
100: '#F7F7FA',
|
||||
50: '#FFFFFF',
|
||||
},
|
||||
purple: {
|
||||
900: '#615BC2',
|
||||
800: '#6C65E5',
|
||||
700: '#817AFE',
|
||||
600: '#8C87EB',
|
||||
500: '#9D98EE',
|
||||
400: '#ADA9F1',
|
||||
300: '#BEBBF3',
|
||||
200: '#CECCF6',
|
||||
100: '#E4E3F9',
|
||||
50: '#F1F0FC',
|
||||
},
|
||||
red: {
|
||||
900: '#A6000E',
|
||||
800: '#D11525',
|
||||
700: '#D93644',
|
||||
500: '#E04C59',
|
||||
400: '#F0616D',
|
||||
300: '#FEB0B7',
|
||||
200: '#FFD4D8',
|
||||
100: '#FFE5E7',
|
||||
50: '#FFF2F3',
|
||||
},
|
||||
orange: {
|
||||
900: '#B35300',
|
||||
800: '#D76500',
|
||||
700: '#F28D00',
|
||||
600: '#FFAA33',
|
||||
500: '#FFC46F',
|
||||
400: '#FFCF8B',
|
||||
300: '#FFD9A3',
|
||||
200: '#FFEACC',
|
||||
100: '#FFF4E5',
|
||||
50: '#FFFCF5',
|
||||
},
|
||||
green: {
|
||||
900: '#305200',
|
||||
800: '#3B6600',
|
||||
700: '#4D8400',
|
||||
500: '#68A611',
|
||||
400: '#7FB435',
|
||||
300: '#99C35D',
|
||||
200: '#CFE5AE',
|
||||
100: '#E4F0D3',
|
||||
50: '#F4FAEB',
|
||||
},
|
||||
blue: {
|
||||
900: '#163E59',
|
||||
800: '#0060A1',
|
||||
700: '#0071BD',
|
||||
600: '#007ACA',
|
||||
500: '#0087E0',
|
||||
400: '#1C98EB',
|
||||
300: '#5BB4F0',
|
||||
200: '#96D2FA',
|
||||
100: '#DCEEFA',
|
||||
50: '#EBF7FF',
|
||||
},
|
||||
} as const;
|
@ -1,4 +1,5 @@
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
import { colors } from './colors';
|
||||
|
||||
export default createTheme({
|
||||
borderRadius: {
|
||||
@ -13,6 +14,10 @@ export default createTheme({
|
||||
fontWeightMedium: '700',
|
||||
allVariants: { lineHeight: 1.4 },
|
||||
button: { lineHeight: 1.75 },
|
||||
h1: {
|
||||
fontSize: '1.5rem',
|
||||
lineHeight: 1.875,
|
||||
},
|
||||
},
|
||||
fontSizes: {
|
||||
mainHeader: '1.2rem',
|
||||
@ -37,15 +42,16 @@ export default createTheme({
|
||||
},
|
||||
palette: {
|
||||
primary: {
|
||||
main: '#635DC5',
|
||||
light: '#817AFE',
|
||||
dark: '#635DC5',
|
||||
main: colors.purple[800],
|
||||
light: colors.purple[700],
|
||||
dark: colors.purple[900],
|
||||
},
|
||||
secondary: {
|
||||
main: '#635DC5',
|
||||
light: '#817AFE',
|
||||
dark: '#635DC5',
|
||||
main: colors.purple[800],
|
||||
light: colors.purple[700],
|
||||
dark: colors.purple[900],
|
||||
},
|
||||
grey: colors.grey,
|
||||
neutral: {
|
||||
main: '#18243e',
|
||||
},
|
||||
@ -61,11 +67,20 @@ export default createTheme({
|
||||
tabs: {
|
||||
main: '#efefef',
|
||||
},
|
||||
error: {
|
||||
main: '#d95e5e',
|
||||
},
|
||||
success: {
|
||||
main: '#3bd86e',
|
||||
light: colors.green[100],
|
||||
main: colors.green[700],
|
||||
dark: colors.green[800],
|
||||
},
|
||||
warning: {
|
||||
light: colors.orange[200],
|
||||
main: colors.orange[700],
|
||||
dark: colors.orange[800],
|
||||
},
|
||||
error: {
|
||||
light: colors.red[200],
|
||||
main: colors.red[700],
|
||||
dark: colors.red[800],
|
||||
},
|
||||
division: {
|
||||
main: '#f1f1f1',
|
||||
@ -74,4 +89,13 @@ export default createTheme({
|
||||
main: '#000',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
MuiTableHead: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
background: colors.grey[200],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -1,9 +0,0 @@
|
||||
export const unleashGrey = {
|
||||
'100': '#f7f7fa',
|
||||
'200': '#f2f2f5',
|
||||
'300': '#eaeaed',
|
||||
'400': '#e1e1e3',
|
||||
'500': '#bdbdbf',
|
||||
'700': '#78787a',
|
||||
'900': '#202021',
|
||||
};
|
134
frontend/src/types/react-table-config.d.ts
vendored
Normal file
134
frontend/src/types/react-table-config.d.ts
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
/**
|
||||
* remove after `react-table` v8
|
||||
* https://github.com/TanStack/react-table/issues/2970#issuecomment-756364081
|
||||
*/
|
||||
|
||||
import {
|
||||
UseColumnOrderInstanceProps,
|
||||
UseColumnOrderState,
|
||||
UseExpandedHooks,
|
||||
UseExpandedInstanceProps,
|
||||
UseExpandedOptions,
|
||||
UseExpandedRowProps,
|
||||
UseExpandedState,
|
||||
UseFiltersColumnOptions,
|
||||
UseFiltersColumnProps,
|
||||
UseFiltersInstanceProps,
|
||||
UseFiltersOptions,
|
||||
UseFiltersState,
|
||||
UseGlobalFiltersColumnOptions,
|
||||
UseGlobalFiltersInstanceProps,
|
||||
UseGlobalFiltersOptions,
|
||||
UseGlobalFiltersState,
|
||||
UseGroupByCellProps,
|
||||
UseGroupByColumnOptions,
|
||||
UseGroupByColumnProps,
|
||||
UseGroupByHooks,
|
||||
UseGroupByInstanceProps,
|
||||
UseGroupByOptions,
|
||||
UseGroupByRowProps,
|
||||
UseGroupByState,
|
||||
UsePaginationInstanceProps,
|
||||
UsePaginationOptions,
|
||||
UsePaginationState,
|
||||
UseResizeColumnsColumnOptions,
|
||||
UseResizeColumnsColumnProps,
|
||||
UseResizeColumnsOptions,
|
||||
UseResizeColumnsState,
|
||||
UseRowSelectHooks,
|
||||
UseRowSelectInstanceProps,
|
||||
UseRowSelectOptions,
|
||||
UseRowSelectRowProps,
|
||||
UseRowSelectState,
|
||||
UseRowStateCellProps,
|
||||
UseRowStateInstanceProps,
|
||||
UseRowStateOptions,
|
||||
UseRowStateRowProps,
|
||||
UseRowStateState,
|
||||
UseSortByColumnOptions,
|
||||
UseSortByColumnProps,
|
||||
UseSortByHooks,
|
||||
UseSortByInstanceProps,
|
||||
UseSortByOptions,
|
||||
UseSortByState,
|
||||
} from 'react-table';
|
||||
|
||||
declare module 'react-table' {
|
||||
// take this file as-is, or comment out the sections that don't apply to your plugin configuration
|
||||
|
||||
export interface TableOptions<
|
||||
D extends Record<string, unknown>
|
||||
> extends UseExpandedOptions<D>,
|
||||
UseFiltersOptions<D>,
|
||||
UseGlobalFiltersOptions<D>,
|
||||
UseGroupByOptions<D>,
|
||||
UsePaginationOptions<D>,
|
||||
UseResizeColumnsOptions<D>,
|
||||
UseRowSelectOptions<D>,
|
||||
UseRowStateOptions<D>,
|
||||
UseSortByOptions<D>,
|
||||
// note that having Record here allows you to add anything to the options, this matches the spirit of the
|
||||
// underlying js library, but might be cleaner if it's replaced by a more specific type that matches your
|
||||
// feature set, this is a safe default.
|
||||
Record<string, any> {}
|
||||
|
||||
export interface Hooks<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
> extends UseExpandedHooks<D>,
|
||||
UseGroupByHooks<D>,
|
||||
UseRowSelectHooks<D>,
|
||||
UseSortByHooks<D> {}
|
||||
|
||||
export interface TableInstance<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
> extends UseColumnOrderInstanceProps<D>,
|
||||
UseExpandedInstanceProps<D>,
|
||||
UseFiltersInstanceProps<D>,
|
||||
UseGlobalFiltersInstanceProps<D>,
|
||||
UseGroupByInstanceProps<D>,
|
||||
UsePaginationInstanceProps<D>,
|
||||
UseRowSelectInstanceProps<D>,
|
||||
UseRowStateInstanceProps<D>,
|
||||
UseSortByInstanceProps<D> {}
|
||||
|
||||
export interface TableState<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
> extends UseColumnOrderState<D>,
|
||||
UseExpandedState<D>,
|
||||
UseFiltersState<D>,
|
||||
UseGlobalFiltersState<D>,
|
||||
UseGroupByState<D>,
|
||||
UsePaginationState<D>,
|
||||
UseResizeColumnsState<D>,
|
||||
UseRowSelectState<D>,
|
||||
UseRowStateState<D>,
|
||||
UseSortByState<D> {}
|
||||
|
||||
export interface ColumnInterface<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
> extends UseFiltersColumnOptions<D>,
|
||||
UseGlobalFiltersColumnOptions<D>,
|
||||
UseGroupByColumnOptions<D>,
|
||||
UseResizeColumnsColumnOptions<D>,
|
||||
UseSortByColumnOptions<D> {}
|
||||
|
||||
export interface ColumnInstance<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
> extends UseFiltersColumnProps<D>,
|
||||
UseGroupByColumnProps<D>,
|
||||
UseResizeColumnsColumnProps<D>,
|
||||
UseSortByColumnProps<D> {}
|
||||
|
||||
export interface Cell<
|
||||
D extends Record<string, unknown> = Record<string, unknown>,
|
||||
V = any
|
||||
> extends UseGroupByCellProps<D>,
|
||||
UseRowStateCellProps<D> {}
|
||||
|
||||
export interface Row<
|
||||
D extends Record<string, unknown> = Record<string, unknown>
|
||||
> extends UseExpandedRowProps<D>,
|
||||
UseGroupByRowProps<D>,
|
||||
UseRowSelectRowProps<D>,
|
||||
UseRowStateRowProps<D> {}
|
||||
}
|
@ -12,7 +12,7 @@ import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
|
||||
import PanToolIcon from '@mui/icons-material/PanTool';
|
||||
import BuildIcon from '@mui/icons-material/Build';
|
||||
|
||||
export const getFeatureTypeIcons = (type: string) => {
|
||||
export const getFeatureTypeIcons = (type?: string) => {
|
||||
switch (type) {
|
||||
case RELEASE:
|
||||
return LoopIcon;
|
||||
|
@ -2222,6 +2222,11 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/history@^4.7.11":
|
||||
version "4.7.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
|
||||
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
|
||||
|
||||
"@types/html-minifier-terser@^6.0.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
|
||||
@ -2367,6 +2372,30 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-router-dom@5.3.3":
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
|
||||
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
|
||||
dependencies:
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
"@types/react-router" "*"
|
||||
|
||||
"@types/react-router@*":
|
||||
version "5.1.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.18.tgz#c8851884b60bc23733500d86c1266e1cfbbd9ef3"
|
||||
integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==
|
||||
dependencies:
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-table@^7.7.11":
|
||||
version "7.7.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.11.tgz#0efbb69aabf5b4b9c26c4c027b1e1ceb0f342303"
|
||||
integrity sha512-Ntfr4EMWgqf/m/CxfmiHww5HvE1nOfK3yEm3NJ3ZWv9IkdteqTOklG3rJtFCtICKAkr3q5pqajkm0y1+WnmdbA==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-test-renderer@17.0.2":
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.2.tgz#5f800a39b12ac8d2a2149e7e1885215bcf4edbbf"
|
||||
@ -8613,6 +8642,11 @@ react-shallow-renderer@^16.13.1:
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
|
||||
|
||||
react-table@^7.7.0:
|
||||
version "7.7.0"
|
||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.7.0.tgz#e2ce14d7fe3a559f7444e9ecfe8231ea8373f912"
|
||||
integrity sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA==
|
||||
|
||||
react-test-renderer@17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
|
||||
|
Loading…
Reference in New Issue
Block a user