mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +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/node": "17.0.18",
|
||||||
"@types/react": "17.0.44",
|
"@types/react": "17.0.44",
|
||||||
"@types/react-dom": "17.0.16",
|
"@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-test-renderer": "17.0.2",
|
||||||
"@types/react-timeago": "4.1.3",
|
"@types/react-timeago": "4.1.3",
|
||||||
"@types/semver": "^7.3.9",
|
"@types/semver": "^7.3.9",
|
||||||
@ -82,6 +84,7 @@
|
|||||||
"react-hooks-global-state": "1.0.2",
|
"react-hooks-global-state": "1.0.2",
|
||||||
"react-router-dom": "6.3.0",
|
"react-router-dom": "6.3.0",
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
|
"react-table": "^7.7.0",
|
||||||
"react-test-renderer": "17.0.2",
|
"react-test-renderer": "17.0.2",
|
||||||
"react-timeago": "6.2.1",
|
"react-timeago": "6.2.1",
|
||||||
"sass": "1.51.0",
|
"sass": "1.51.0",
|
||||||
|
@ -32,7 +32,7 @@ const UsersAdmin = () => {
|
|||||||
show={
|
show={
|
||||||
<div className={styles.tableActions}>
|
<div className={styles.tableActions}>
|
||||||
<TableActions
|
<TableActions
|
||||||
search={search}
|
initialSearchValue={search}
|
||||||
onSearch={search =>
|
onSearch={search =>
|
||||||
setSearch(search)
|
setSearch(search)
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
import { makeStyles } from 'tss-react/mui';
|
import { makeStyles } from 'tss-react/mui';
|
||||||
import { unleashGrey } from 'themes/themeColors';
|
|
||||||
|
|
||||||
export const useStyles = makeStyles()(theme => ({
|
export const useStyles = makeStyles()(theme => ({
|
||||||
tableRow: {
|
tableRow: {
|
||||||
'& > td': {
|
'& > td': {
|
||||||
padding: '4px 16px',
|
padding: '4px 16px',
|
||||||
borderColor: unleashGrey[300],
|
borderColor: theme.palette.grey[300],
|
||||||
},
|
},
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: unleashGrey[100],
|
backgroundColor: theme.palette.grey[100],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
tableCellHeader: {
|
tableCellHeader: {
|
||||||
'& > th': {
|
'& > th': {
|
||||||
backgroundColor: unleashGrey[200],
|
backgroundColor: theme.palette.grey[200],
|
||||||
fontWeight: 'normal',
|
fontWeight: 'normal',
|
||||||
border: 0,
|
border: 0,
|
||||||
'&:first-of-type': {
|
'&:first-of-type': {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useFeaturesArchive } from 'hooks/api/getters/useFeaturesArchive/useFeaturesArchive';
|
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 useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||||
import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi';
|
import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { useProjectFeaturesArchive } from 'hooks/api/getters/useProjectFeaturesArchive/useProjectFeaturesArchive';
|
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 useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||||
import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi';
|
import { useFeatureArchiveApi } from 'hooks/api/actions/useFeatureArchiveApi/useReviveFeatureApi';
|
||||||
|
@ -1,16 +1,26 @@
|
|||||||
|
import { VFC } from 'react';
|
||||||
|
|
||||||
interface IHighlighterProps {
|
interface IHighlighterProps {
|
||||||
search: string;
|
search?: string;
|
||||||
children: string;
|
children?: string;
|
||||||
caseSensitive?: boolean;
|
caseSensitive?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
const escapeRegex = (str: string) => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
|
||||||
export const Highlighter = ({
|
export const Highlighter: VFC<IHighlighterProps> = ({
|
||||||
search,
|
search,
|
||||||
children,
|
children,
|
||||||
caseSensitive,
|
caseSensitive,
|
||||||
}: IHighlighterProps) => {
|
}) => {
|
||||||
|
if (!children) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!search) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
const regex = new RegExp(escapeRegex(search), caseSensitive ? 'g' : 'gi');
|
const regex = new RegExp(escapeRegex(search), caseSensitive ? 'g' : 'gi');
|
||||||
|
|
||||||
return (
|
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;
|
style?: React.CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
const PaginateUI = ({
|
const PaginateUI = ({
|
||||||
pages,
|
pages,
|
||||||
pageIndex,
|
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 { makeStyles } from 'tss-react/mui';
|
||||||
import { unleashGrey } from 'themes/themeColors';
|
|
||||||
|
|
||||||
export const useStyles = makeStyles()(theme => ({
|
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: {
|
fieldWidth: {
|
||||||
width: '49px',
|
width: '45px',
|
||||||
'& .search-icon': {
|
'& .search-icon': {
|
||||||
marginRight: 0,
|
marginRight: 0,
|
||||||
},
|
},
|
||||||
@ -19,7 +28,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
fieldWidthEnter: {
|
fieldWidthEnter: {
|
||||||
width: '100%',
|
width: '250px',
|
||||||
transition: 'width 0.6s',
|
transition: 'width 0.6s',
|
||||||
'& .search-icon': {
|
'& .search-icon': {
|
||||||
marginRight: '8px',
|
marginRight: '8px',
|
||||||
@ -37,7 +46,7 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
fieldWidthLeave: {
|
fieldWidthLeave: {
|
||||||
width: '49px',
|
width: '45px',
|
||||||
transition: 'width 0.6s',
|
transition: 'width 0.6s',
|
||||||
'& .search-icon': {
|
'& .search-icon': {
|
||||||
marginRight: 0,
|
marginRight: 0,
|
||||||
@ -53,11 +62,11 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
},
|
},
|
||||||
verticalSeparator: {
|
verticalSeparator: {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
backgroundColor: unleashGrey[500],
|
backgroundColor: theme.palette.grey[500],
|
||||||
width: '1px',
|
width: '1px',
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
marginLeft: '16px',
|
marginLeft: theme.spacing(2),
|
||||||
marginRight: '32px',
|
marginRight: theme.spacing(4),
|
||||||
padding: '10px 0',
|
padding: '10px 0',
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
},
|
},
|
||||||
|
@ -1,60 +1,87 @@
|
|||||||
import { useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
import { IconButton, Tooltip } from '@mui/material';
|
import { IconButton, Tooltip } from '@mui/material';
|
||||||
import { Search } from '@mui/icons-material';
|
import { Search } from '@mui/icons-material';
|
||||||
|
import { useAsyncDebounce } from 'react-table';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import AnimateOnMount from 'component/common/AnimateOnMount/AnimateOnMount';
|
import AnimateOnMount from 'component/common/AnimateOnMount/AnimateOnMount';
|
||||||
import { TableSearchField } from 'component/common/Table/TableActions/TableSearchField/TableSearchField';
|
import { TableSearchField } from './TableSearchField/TableSearchField';
|
||||||
import { useStyles } from 'component/common/Table/TableActions/TableActions.styles';
|
import { useStyles } from './TableActions.styles';
|
||||||
|
|
||||||
interface ITableActionsProps {
|
interface ITableActionsProps {
|
||||||
search: string;
|
initialSearchValue?: string;
|
||||||
onSearch: (value: string) => void;
|
onSearch?: (value: string) => void;
|
||||||
|
searchTip?: string;
|
||||||
|
isSeparated?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TableActions = ({ search, onSearch }: ITableActionsProps) => {
|
export const TableActions: FC<ITableActionsProps> = ({
|
||||||
const [searchExpanded, setSearchExpanded] = useState(false);
|
initialSearchValue: search,
|
||||||
|
onSearch = () => {},
|
||||||
|
searchTip = 'Search',
|
||||||
|
children,
|
||||||
|
isSeparated,
|
||||||
|
}) => {
|
||||||
|
const [searchExpanded, setSearchExpanded] = useState(Boolean(search));
|
||||||
|
const [searchInputState, setSearchInputState] = useState(search);
|
||||||
const [animating, setAnimating] = useState(false);
|
const [animating, setAnimating] = useState(false);
|
||||||
|
const debouncedOnSearch = useAsyncDebounce(onSearch, 200);
|
||||||
|
|
||||||
const { classes: styles } = useStyles();
|
const { classes: styles } = useStyles();
|
||||||
|
|
||||||
const onBlur = (clear = false) => {
|
const onBlur = (clear = false) => {
|
||||||
if (!search || clear) {
|
if (!searchInputState || clear) {
|
||||||
setSearchExpanded(false);
|
setSearchExpanded(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onSearchChange = (value: string) => {
|
||||||
|
debouncedOnSearch(value);
|
||||||
|
setSearchInputState(value);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className={styles.tableActions}>
|
||||||
<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>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={!searchExpanded && !animating}
|
condition={Boolean(onSearch)}
|
||||||
show={
|
show={
|
||||||
<Tooltip title="Search users" arrow>
|
<>
|
||||||
<IconButton
|
<AnimateOnMount
|
||||||
aria-label="Search users"
|
mounted={searchExpanded}
|
||||||
onClick={() => setSearchExpanded(true)}
|
start={styles.fieldWidth}
|
||||||
size="large"
|
enter={styles.fieldWidthEnter}
|
||||||
|
leave={styles.fieldWidthLeave}
|
||||||
|
onStart={() => setAnimating(true)}
|
||||||
|
onEnd={() => setAnimating(false)}
|
||||||
>
|
>
|
||||||
<Search />
|
<TableSearchField
|
||||||
</IconButton>
|
value={searchInputState!}
|
||||||
</Tooltip>
|
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 { IconButton, InputBase, Tooltip } from '@mui/material';
|
||||||
import { Search, Close } from '@mui/icons-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 classnames from 'classnames';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import { useStyles } from './TableSearchField.styles';
|
||||||
|
|
||||||
interface ITableSearchFieldProps {
|
interface ITableSearchFieldProps {
|
||||||
value: string;
|
value?: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@ -13,7 +13,7 @@ interface ITableSearchFieldProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TableSearchField = ({
|
export const TableSearchField = ({
|
||||||
value,
|
value = '',
|
||||||
onChange,
|
onChange,
|
||||||
className,
|
className,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { makeStyles } from 'tss-react/mui';
|
import { makeStyles } from 'tss-react/mui';
|
||||||
import { unleashGrey } from 'themes/themeColors';
|
|
||||||
|
|
||||||
export const useStyles = makeStyles()(theme => ({
|
export const useStyles = makeStyles()(theme => ({
|
||||||
tableCellHeaderSortable: {
|
tableCellHeaderSortable: {
|
||||||
@ -9,13 +8,13 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
'& > svg': {
|
'& > svg': {
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
color: unleashGrey[700],
|
color: theme.palette.grey[700],
|
||||||
marginLeft: '4px',
|
marginLeft: '4px',
|
||||||
},
|
},
|
||||||
'&.sorted': {
|
'&.sorted': {
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
'& > svg': {
|
'& > svg': {
|
||||||
color: unleashGrey[900],
|
color: theme.palette.grey[900],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -29,9 +28,9 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: unleashGrey[400],
|
backgroundColor: theme.palette.grey[400],
|
||||||
'& > svg': {
|
'& > svg': {
|
||||||
color: unleashGrey[900],
|
color: theme.palette.grey[900],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -23,6 +23,9 @@ interface ITableCellSortableProps {
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated No longer in use. See `SortableTableHeader`. Remove when Users table is refactored.
|
||||||
|
*/
|
||||||
export const TableCellSortable = ({
|
export const TableCellSortable = ({
|
||||||
className,
|
className,
|
||||||
name,
|
name,
|
||||||
@ -40,12 +43,6 @@ export const TableCellSortable = ({
|
|||||||
: 'ascending'
|
: 'ascending'
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const cellClassName = classnames(
|
|
||||||
className,
|
|
||||||
styles.tableCellHeaderSortable,
|
|
||||||
sort.type === name && 'sorted'
|
|
||||||
);
|
|
||||||
|
|
||||||
const onSortClick = () => {
|
const onSortClick = () => {
|
||||||
setSort(prev => ({
|
setSort(prev => ({
|
||||||
desc: !Boolean(prev.desc),
|
desc: !Boolean(prev.desc),
|
||||||
@ -57,7 +54,14 @@ export const TableCellSortable = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
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}>
|
<button className={styles.sortButton} onClick={onSortClick}>
|
||||||
{children}
|
{children}
|
||||||
<ConditionallyRender
|
<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',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
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 PageContent from 'component/common/PageContent/PageContent';
|
||||||
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
import { HeaderTitle } from 'component/common/HeaderTitle/HeaderTitle';
|
||||||
import AccessContext from 'contexts/AccessContext';
|
import AccessContext from 'contexts/AccessContext';
|
||||||
import ListPlaceholder from 'component/common/ListPlaceholder/ListPlaceholder';
|
|
||||||
import { IFeaturesFilter } from 'hooks/useFeaturesFilter';
|
import { IFeaturesFilter } from 'hooks/useFeaturesFilter';
|
||||||
import { FeatureToggleListItem } from './FeatureToggleListItem/FeatureToggleListItem';
|
import { FeatureToggleListItem } from './FeatureToggleListItem/FeatureToggleListItem';
|
||||||
import { FeatureToggleListActions } from './FeatureToggleListActions/FeatureToggleListActions';
|
import { FeatureToggleListActions } from './FeatureToggleListActions/FeatureToggleListActions';
|
||||||
import { CreateFeatureButton } from '../CreateFeatureButton/CreateFeatureButton';
|
import { CreateFeatureButton } from '../CreateFeatureButton/CreateFeatureButton';
|
||||||
import { useCreateFeaturePath } from '../CreateFeatureButton/useCreateFeaturePath';
|
|
||||||
import { IFeaturesSort } from 'hooks/useFeaturesSort';
|
import { IFeaturesSort } from 'hooks/useFeaturesSort';
|
||||||
import { FeatureSchema } from 'openapi';
|
import { FeatureSchema } from 'openapi';
|
||||||
import { useStyles } from './styles';
|
import { useStyles } from './styles';
|
||||||
@ -63,7 +61,6 @@ export const FeatureToggleList: VFC<IFeatureToggleListProps> = ({
|
|||||||
setSort,
|
setSort,
|
||||||
}) => {
|
}) => {
|
||||||
const { hasAccess } = useContext(AccessContext);
|
const { hasAccess } = useContext(AccessContext);
|
||||||
const createFeature = useCreateFeaturePath(filter);
|
|
||||||
const { classes: styles } = useStyles();
|
const { classes: styles } = useStyles();
|
||||||
const smallScreen = useMediaQuery('(max-width:800px)');
|
const smallScreen = useMediaQuery('(max-width:800px)');
|
||||||
const mobileView = useMediaQuery('(max-width:600px)');
|
const mobileView = useMediaQuery('(max-width:600px)');
|
||||||
@ -101,26 +98,9 @@ export const FeatureToggleList: VFC<IFeatureToggleListProps> = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
elseShow={
|
elseShow={
|
||||||
<ConditionallyRender
|
<ListItem className={styles.emptyStateListItem}>
|
||||||
condition={Boolean(isArchive)}
|
No archived features.
|
||||||
show={
|
</ListItem>
|
||||||
<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"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
@ -1,24 +1,22 @@
|
|||||||
import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures';
|
import { useFeatures } from 'hooks/api/getters/useFeatures/useFeatures';
|
||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import { FeatureSchema } from 'openapi';
|
||||||
import { useFeaturesFilter } from 'hooks/useFeaturesFilter';
|
import { FeatureToggleListTable } from './FeatureToggleListTable/FeatureToggleListTable';
|
||||||
import { FeatureToggleList } from './FeatureToggleList';
|
|
||||||
import { useFeaturesSort } from 'hooks/useFeaturesSort';
|
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 = () => {
|
export const FeatureToggleListContainer = () => {
|
||||||
const { uiConfig } = useUiConfig();
|
|
||||||
const { features = [], loading } = useFeatures();
|
const { features = [], loading } = useFeatures();
|
||||||
const { filtered, filter, setFilter } = useFeaturesFilter(features);
|
|
||||||
const { sorted, sort, setSort } = useFeaturesSort(filtered);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FeatureToggleList
|
<FeatureToggleListTable
|
||||||
features={sorted}
|
data={loading ? featuresPlaceholder : features}
|
||||||
loading={loading}
|
isLoading={loading}
|
||||||
flags={uiConfig.flags}
|
|
||||||
filter={filter}
|
|
||||||
setFilter={setFilter}
|
|
||||||
sort={sort}
|
|
||||||
setSort={setSort}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -27,6 +27,9 @@ interface IFeatureToggleListItemProps {
|
|||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
export const FeatureToggleListItem = memo<IFeatureToggleListItemProps>(
|
export const FeatureToggleListItem = memo<IFeatureToggleListItemProps>(
|
||||||
({
|
({
|
||||||
feature,
|
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 }
|
{ field: 'name', type: 'string', direction: 0 }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
const FeatureToggleListNew = ({
|
const FeatureToggleListNew = ({
|
||||||
features,
|
features,
|
||||||
loading,
|
loading,
|
||||||
|
@ -7,7 +7,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
class="tss-fdcp7c-container"
|
class="tss-fdcp7c-container"
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
class="tss-16bfuiy-title"
|
class="tss-1a5bydb-title"
|
||||||
>
|
>
|
||||||
Please help us improve
|
Please help us improve
|
||||||
</h1>
|
</h1>
|
||||||
@ -24,12 +24,12 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
class="tss-io6e1g-scoreInput"
|
class="tss-io6e1g-scoreInput"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="tss-lys30y-scoreHelp"
|
class="tss-b4a690-scoreHelp"
|
||||||
>
|
>
|
||||||
Very difficult
|
Very difficult
|
||||||
</span>
|
</span>
|
||||||
<label
|
<label
|
||||||
class="tss-mulaxt-scoreValue"
|
class="tss-pq8zjr-scoreValue"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
name="score"
|
name="score"
|
||||||
@ -41,7 +41,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
class="tss-mulaxt-scoreValue"
|
class="tss-pq8zjr-scoreValue"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
name="score"
|
name="score"
|
||||||
@ -53,7 +53,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
class="tss-mulaxt-scoreValue"
|
class="tss-pq8zjr-scoreValue"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
name="score"
|
name="score"
|
||||||
@ -65,7 +65,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
class="tss-mulaxt-scoreValue"
|
class="tss-pq8zjr-scoreValue"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
name="score"
|
name="score"
|
||||||
@ -77,7 +77,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
class="tss-mulaxt-scoreValue"
|
class="tss-pq8zjr-scoreValue"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
name="score"
|
name="score"
|
||||||
@ -89,7 +89,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
class="tss-mulaxt-scoreValue"
|
class="tss-pq8zjr-scoreValue"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
name="score"
|
name="score"
|
||||||
@ -101,7 +101,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<label
|
<label
|
||||||
class="tss-mulaxt-scoreValue"
|
class="tss-pq8zjr-scoreValue"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
name="score"
|
name="score"
|
||||||
@ -113,7 +113,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<span
|
<span
|
||||||
class="tss-lys30y-scoreHelp"
|
class="tss-b4a690-scoreHelp"
|
||||||
>
|
>
|
||||||
Very easy
|
Very easy
|
||||||
</span>
|
</span>
|
||||||
@ -131,7 +131,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
class="MuiFormControl-root MuiFormControl-fullWidth MuiTextField-root mui-wb57ya-MuiFormControl-root-MuiTextField-root"
|
class="MuiFormControl-root MuiFormControl-fullWidth MuiTextField-root mui-wb57ya-MuiFormControl-root-MuiTextField-root"
|
||||||
>
|
>
|
||||||
<div
|
<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
|
<textarea
|
||||||
aria-invalid="false"
|
aria-invalid="false"
|
||||||
@ -169,7 +169,7 @@ exports[`FeedbackCESForm 1`] = `
|
|||||||
hidden=""
|
hidden=""
|
||||||
>
|
>
|
||||||
<button
|
<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=""
|
disabled=""
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
type="submit"
|
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"
|
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||||
>
|
>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -71,7 +71,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -90,7 +90,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -110,7 +110,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -130,7 +130,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -149,7 +149,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -168,7 +168,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -187,7 +187,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -223,7 +223,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||||
>
|
>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -242,7 +242,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -261,7 +261,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -280,7 +280,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -299,7 +299,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -333,7 +333,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||||
>
|
>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -352,7 +352,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -371,7 +371,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -390,7 +390,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -409,7 +409,7 @@ exports[`should render DrawerMenu 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<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"
|
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||||
>
|
>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -507,7 +507,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -526,7 +526,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -546,7 +546,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -566,7 +566,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -585,7 +585,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -604,7 +604,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -623,7 +623,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<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"
|
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||||
>
|
>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -678,7 +678,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -697,7 +697,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -716,7 +716,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -735,7 +735,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<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"
|
className="MuiList-root MuiList-padding MuiList-dense tss-11htu0j-list mui-h4y409-MuiList-root"
|
||||||
>
|
>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -788,7 +788,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -807,7 +807,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -826,7 +826,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -845,7 +845,7 @@ exports[`should render DrawerMenu with "features" selected 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -12,7 +12,7 @@ Array [
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="tss-16iyfoa-headerContainer"
|
className="tss-8bhpw1-headerContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="tss-14s7qul-headerTitleContainer"
|
className="tss-14s7qul-headerTitleContainer"
|
||||||
@ -22,7 +22,7 @@ Array [
|
|||||||
data-loading={true}
|
data-loading={true}
|
||||||
>
|
>
|
||||||
<h1
|
<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
|
Strategies
|
||||||
</h1>
|
</h1>
|
||||||
@ -35,7 +35,7 @@ Array [
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
aria-describedby="useId-0"
|
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"
|
data-testid="ADD_NEW_STRATEGY_ID"
|
||||||
disabled={false}
|
disabled={false}
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
@ -73,7 +73,7 @@ Array [
|
|||||||
className="MuiList-root MuiList-padding mui-h4y409-MuiList-root"
|
className="MuiList-root MuiList-padding mui-h4y409-MuiList-root"
|
||||||
>
|
>
|
||||||
<li
|
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -11,7 +11,7 @@ exports[`renders an empty list correctly 1`] = `
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="tss-16iyfoa-headerContainer"
|
className="tss-8bhpw1-headerContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="tss-14s7qul-headerTitleContainer"
|
className="tss-14s7qul-headerTitleContainer"
|
||||||
@ -21,7 +21,7 @@ exports[`renders an empty list correctly 1`] = `
|
|||||||
data-loading={true}
|
data-loading={true}
|
||||||
>
|
>
|
||||||
<h1
|
<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
|
Tag Types
|
||||||
</h1>
|
</h1>
|
||||||
@ -30,7 +30,7 @@ exports[`renders an empty list correctly 1`] = `
|
|||||||
className="tss-ap2nhp-headerActions"
|
className="tss-ap2nhp-headerActions"
|
||||||
>
|
>
|
||||||
<button
|
<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}
|
disabled={false}
|
||||||
onBlur={[Function]}
|
onBlur={[Function]}
|
||||||
onClick={[Function]}
|
onClick={[Function]}
|
||||||
@ -60,7 +60,7 @@ exports[`renders an empty list correctly 1`] = `
|
|||||||
className="MuiList-root MuiList-padding mui-h4y409-MuiList-root"
|
className="MuiList-root MuiList-padding mui-h4y409-MuiList-root"
|
||||||
>
|
>
|
||||||
<li
|
<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}
|
disabled={false}
|
||||||
>
|
>
|
||||||
No entries
|
No entries
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { paginate } from 'utils/paginate';
|
import { paginate } from 'utils/paginate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
const usePagination = <T>(
|
const usePagination = <T>(
|
||||||
data: T[],
|
data: T[],
|
||||||
limit: number,
|
limit: number,
|
||||||
|
@ -15,6 +15,9 @@ import {
|
|||||||
import { ReportingSortType } from 'component/Reporting/constants';
|
import { ReportingSortType } from 'component/Reporting/constants';
|
||||||
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
import { IFeatureToggleListItem } from 'interfaces/featureToggle';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
const useSort = () => {
|
const useSort = () => {
|
||||||
const [sortData, setSortData] = useState<{
|
const [sortData, setSortData] = useState<{
|
||||||
sortKey: ReportingSortType;
|
sortKey: ReportingSortType;
|
||||||
|
@ -38,6 +38,7 @@ button {
|
|||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
fill: none;
|
fill: none;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton::before {
|
.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 { createTheme } from '@mui/material/styles';
|
||||||
|
import { colors } from './colors';
|
||||||
|
|
||||||
export default createTheme({
|
export default createTheme({
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
@ -13,6 +14,10 @@ export default createTheme({
|
|||||||
fontWeightMedium: '700',
|
fontWeightMedium: '700',
|
||||||
allVariants: { lineHeight: 1.4 },
|
allVariants: { lineHeight: 1.4 },
|
||||||
button: { lineHeight: 1.75 },
|
button: { lineHeight: 1.75 },
|
||||||
|
h1: {
|
||||||
|
fontSize: '1.5rem',
|
||||||
|
lineHeight: 1.875,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
fontSizes: {
|
fontSizes: {
|
||||||
mainHeader: '1.2rem',
|
mainHeader: '1.2rem',
|
||||||
@ -37,15 +42,16 @@ export default createTheme({
|
|||||||
},
|
},
|
||||||
palette: {
|
palette: {
|
||||||
primary: {
|
primary: {
|
||||||
main: '#635DC5',
|
main: colors.purple[800],
|
||||||
light: '#817AFE',
|
light: colors.purple[700],
|
||||||
dark: '#635DC5',
|
dark: colors.purple[900],
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: '#635DC5',
|
main: colors.purple[800],
|
||||||
light: '#817AFE',
|
light: colors.purple[700],
|
||||||
dark: '#635DC5',
|
dark: colors.purple[900],
|
||||||
},
|
},
|
||||||
|
grey: colors.grey,
|
||||||
neutral: {
|
neutral: {
|
||||||
main: '#18243e',
|
main: '#18243e',
|
||||||
},
|
},
|
||||||
@ -61,11 +67,20 @@ export default createTheme({
|
|||||||
tabs: {
|
tabs: {
|
||||||
main: '#efefef',
|
main: '#efefef',
|
||||||
},
|
},
|
||||||
error: {
|
|
||||||
main: '#d95e5e',
|
|
||||||
},
|
|
||||||
success: {
|
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: {
|
division: {
|
||||||
main: '#f1f1f1',
|
main: '#f1f1f1',
|
||||||
@ -74,4 +89,13 @@ export default createTheme({
|
|||||||
main: '#000',
|
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 PanToolIcon from '@mui/icons-material/PanTool';
|
||||||
import BuildIcon from '@mui/icons-material/Build';
|
import BuildIcon from '@mui/icons-material/Build';
|
||||||
|
|
||||||
export const getFeatureTypeIcons = (type: string) => {
|
export const getFeatureTypeIcons = (type?: string) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case RELEASE:
|
case RELEASE:
|
||||||
return LoopIcon;
|
return LoopIcon;
|
||||||
|
@ -2222,6 +2222,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@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":
|
"@types/html-minifier-terser@^6.0.0":
|
||||||
version "6.1.0"
|
version "6.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
|
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
|
||||||
@ -2367,6 +2372,30 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@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":
|
"@types/react-test-renderer@17.0.2":
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.2.tgz#5f800a39b12ac8d2a2149e7e1885215bcf4edbbf"
|
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"
|
object-assign "^4.1.1"
|
||||||
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
|
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:
|
react-test-renderer@17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
|
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c"
|
||||||
|
Loading…
Reference in New Issue
Block a user