@material-ui/core#Menu TypeScript Examples
The following examples show how to use
@material-ui/core#Menu.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: LoginIcon.tsx From cognitive-search-static-web-apps-sample-ui with MIT License | 6 votes |
render(): JSX.Element {
const state = this.props.state;
return (<>
<Button color={state.isLoggedInAnonymously ? "secondary" : "inherit"}
onClick={evt => state.menuAnchorElement = evt.currentTarget}
>
<Tooltip title={state.isLoggedInAnonymously ? "ANONYMOUS" : state.userName}>
<AccountCircle />
</Tooltip>
</Button>
<Menu
anchorEl={state.menuAnchorElement}
open={!state.isLoggedInAnonymously && !!state.menuAnchorElement}
onClose={() => state.menuAnchorElement = undefined}
>
<MenuItem onClick={() => state.logout()}>Login under a different name</MenuItem>
</Menu>
</>);
}
Example #2
Source File: MoreMenu.tsx From abacus with GNU General Public License v2.0 | 6 votes |
export default function MoreMenu({ children }: { children: React.ReactNode }): JSX.Element {
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null)
const open = Boolean(anchorEl)
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget)
}
const handleClose = () => {
setAnchorEl(null)
}
return (
<div>
<IconButton aria-label='more' aria-controls='menu' aria-haspopup='true' onClick={handleClick}>
<MoreVertIcon />
</IconButton>
<Menu id='menu' anchorEl={anchorEl} keepMounted open={open} onClose={handleClose} onClick={handleClose}>
{children}
</Menu>
</div>
)
}
Example #3
Source File: context_menu.tsx From jupyter-extensions with Apache License 2.0 | 6 votes |
ContextMenuContainer = withStyles({
paper: {
border: '1px solid var(--jp-border-color0)',
borderRadius: 0,
},
list: {
padding: 0,
},
})(Menu)
Example #4
Source File: UserSettingsMenu.tsx From backstage with Apache License 2.0 | 6 votes |
UserSettingsMenu = () => {
const identityApi = useApi(identityApiRef);
const [open, setOpen] = React.useState(false);
const [anchorEl, setAnchorEl] = React.useState<undefined | HTMLElement>(
undefined,
);
const handleOpen = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
setOpen(true);
};
const handleClose = () => {
setAnchorEl(undefined);
setOpen(false);
};
return (
<>
<IconButton
data-testid="user-settings-menu"
aria-label="more"
onClick={handleOpen}
>
<MoreVertIcon />
</IconButton>
<Menu anchorEl={anchorEl} open={open} onClose={handleClose}>
<MenuItem data-testid="sign-out" onClick={() => identityApi.signOut()}>
<ListItemIcon>
<SignOutIcon />
</ListItemIcon>
Sign Out
</MenuItem>
</Menu>
</>
);
}
Example #5
Source File: icon_button_menu.tsx From jupyter-extensions with Apache License 2.0 | 6 votes |
render() {
const { icon, menuItems } = this.props;
const { anchorEl } = this.state;
const iconElement = icon || <MoreVert />;
return (
<span>
<IconButton onClick={e => this._onOpenMenu(e)}>
{iconElement}
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
keepMounted
onClose={this._onMenuClose}
>
{menuItems(this._onMenuClose)}
</Menu>
</span>
);
}
Example #6
Source File: SelectStamp.tsx From bee-dashboard with BSD 3-Clause "New" or "Revised" License | 6 votes |
export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props): ReactElement | null {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null)
if (!stamps) return null
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget)
}
const handleClose = () => setAnchorEl(null)
return (
<div>
<Button variant="contained" aria-haspopup="true" onClick={handleClick}>
Change
</Button>
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
{stamps.map(stamp => (
<MenuItem
key={stamp.batchID}
onClick={() => {
setSelected(stamp)
handleClose()
}}
selected={stamp.batchID === selectedStamp?.batchID}
>
<ListItemIcon>{stamp.usageText}</ListItemIcon>
<Typography variant="body2">{stamp.batchID.slice(0, 8)}[…]</Typography>
</MenuItem>
))}
</Menu>
</div>
)
}
Example #7
Source File: Table.stories.tsx From kodiak-ui with MIT License | 5 votes |
function Actions({
onActionSelect,
}: {
onActionSelect: (value: string) => void
}) {
const [anchorEl, setAnchorEl] = React.useState(null)
const handleClick = (event: any) => {
setAnchorEl(event.currentTarget)
}
const handleClose = () => {
setAnchorEl(null)
}
const handleSelect = (value: string) => {
onActionSelect && onActionSelect(value)
handleClose()
}
return (
<>
<Tooltip title="Actions">
<IconButton aria-label="actions" onClick={handleClick}>
<MoreVertIcon />
</IconButton>
</Tooltip>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem onClick={() => handleSelect('Duplicate')}>
<ListItemIcon>
<FileCopyIcon />
</ListItemIcon>
<ListItemText primary="Duplicate" />
</MenuItem>
<MenuItem onClick={() => handleSelect('Delete')}>
<ListItemIcon>
<DeleteIcon />
</ListItemIcon>
<ListItemText primary="Delete" />
</MenuItem>
</Menu>
</>
)
}
Example #8
Source File: TechDocsReaderPageSubheader.tsx From backstage with Apache License 2.0 | 5 votes |
TechDocsReaderPageSubheader = ({
toolbarProps,
}: {
toolbarProps?: ToolbarProps;
}) => {
const classes = useStyles();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const handleClick = useCallback((event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
}, []);
const handleClose = useCallback(() => {
setAnchorEl(null);
}, []);
const {
entityMetadata: { value: entityMetadata, loading: entityMetadataLoading },
} = useTechDocsReaderPage();
const addons = useTechDocsAddons();
const subheaderAddons = addons.renderComponentsByLocation(
locations.Subheader,
);
const settingsAddons = addons.renderComponentsByLocation(locations.Settings);
if (!subheaderAddons && !settingsAddons) return null;
// No entity metadata = 404. Don't render subheader on 404.
if (entityMetadataLoading === false && !entityMetadata) return null;
return (
<Toolbar classes={classes} {...toolbarProps}>
<Box
display="flex"
justifyContent="flex-end"
width="100%"
flexWrap="wrap"
>
{subheaderAddons}
{settingsAddons ? (
<>
<Tooltip title="Settings">
<IconButton
aria-controls="tech-docs-reader-page-settings"
aria-haspopup="true"
onClick={handleClick}
>
<SettingsIcon />
</IconButton>
</Tooltip>
<Menu
id="tech-docs-reader-page-settings"
getContentAnchorEl={null}
anchorEl={anchorEl}
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
open={Boolean(anchorEl)}
onClose={handleClose}
keepMounted
>
{settingsAddons}
</Menu>
</>
) : null}
</Box>
</Toolbar>
);
}
Example #9
Source File: with-custom-menu.tsx From react-component-library with BSD 3-Clause "New" or "Revised" License | 5 votes |
withCustomMenu = (): StoryFnReactReturnType => {
const avatar = <Avatar src={tRex} alt={'User Avatar'} />;
const open = (): void => {
store.set({ open: true });
};
const close = (): void => {
store.set({ open: false });
};
const menu = (state: any): JSX.Element => (
<Menu open={state.open} onClose={close}>
<div key={'header'} style={{ position: 'relative', padding: 10 }}>
<Typography variant={'h6'}>Welcome, </Typography>
<Typography style={{ fontWeight: 600, marginTop: '-10px' }} variant={'h3'}>
T-Rex
</Typography>
<div
style={{
position: 'absolute',
right: 0,
top: 0,
height: '100%',
width: '100%',
opacity: 0.2,
backgroundSize: 'cover',
backgroundImage: `url(${tRex})`,
}}
/>
</div>
<Divider key={'divider-1'} />
<MenuItem onClick={close} key={'account'}>
My Account
</MenuItem>
<MenuItem onClick={close} key={'logout'}>
Logout
</MenuItem>
<Divider key={'divider-2'} />
<img
key={'footer'}
alt={'tRex'}
style={{ textAlign: 'center', padding: '12px 16px 0 16px', height: 40 }}
src={EatonLogo}
/>
</Menu>
);
store.set({ open: false });
return (
<State store={store}>
{(state): JSX.Element => <UserMenu avatar={avatar} onOpen={open} menu={menu(state)} />}
</State>
);
}
Example #10
Source File: Layout.tsx From Demae with MIT License | 5 votes |
AccountMenu = () => {
const [user] = useUser()
const [roles] = useRoles()
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
const menuOpen = Boolean(anchorEl)
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
}
const handleClose = () => {
setAnchorEl(null);
}
if (user) {
return (
<>
<IconButton
onClick={handleMenu}
color="inherit"
>
<AccountCircle />
</IconButton>
<Menu
style={{ width: "120px" }}
anchorEl={anchorEl}
anchorOrigin={{ vertical: "top", horizontal: "right", }}
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right", }}
open={menuOpen}
onClose={handleClose}
>
{roles.map(role => <UserMenuItem key={role.id} role={role} />)}
<Divider />
<MenuItem key={"signout"} onClick={async () => {
await firebase.auth().signOut()
}}>SignOut</MenuItem>
</Menu>
</>
)
} else {
return (
<IconButton
color="inherit"
>
<AccountCircle />
</IconButton>
)
}
}
Example #11
Source File: ContextMenu.tsx From project-tauntaun with GNU Lesser General Public License v3.0 | 5 votes |
export function ContextMenu(props: ContextMenuProps) {
const { options, position, onOptionSelected } = props;
const [visible, setVisible] = useState(true);
const [savedPosition, setSavedPosition] = useState(position);
if (position !== savedPosition) {
setSavedPosition(position);
if (!visible) {
setVisible(true);
}
}
const left = position ? position.xy.x : 0;
const top = position ? position.xy.y : 0;
const handleClose = () => {
setVisible(false);
};
const handleClick = (value: string) => {
onOptionSelected?.(value, position ? position : defaultClickPositionValue);
setVisible(false);
};
return (
<Menu
id="simple-menu"
anchorPosition={{ left: left, top: top }}
anchorReference="anchorPosition"
open={visible}
onClose={handleClose}
onContextMenu={handleClose}
>
{options.map((option, index) => (
<MenuItem key={`ContextMenuItem ${index}`} onClick={() => handleClick(option.value)}>
{option.label}
</MenuItem>
))}
<MenuItem onClick={handleClose}>Close</MenuItem>
</Menu>
);
}
Example #12
Source File: ControlsSummaryFeatureMenu.tsx From Teyvat.moe with GNU General Public License v3.0 | 5 votes |
_ControlsSummaryFeatureMenu: FunctionComponent<ControlsSummaryFeatureMenuProps> = ({
featureKey,
displayed,
hideFeature,
showFeature,
clearAllFeature,
clearExpiredFeature,
locateFeature,
}) => {
const classes = useStyles();
const mapFeature = getMapFeature(featureKey);
const doesExpire = (mapFeature.respawn ?? 'none') !== 'none';
const [menuAnchor, setMenuAnchor] = useState<HTMLButtonElement | null>(null);
const handleOpen: React.MouseEventHandler<HTMLButtonElement> = (event) => {
setMenuAnchor(event.currentTarget);
};
const handleClose = () => {
setMenuAnchor(null);
};
return (
<>
<Tooltip place="left" />
<IconButton classes={{ root: classes.menuButtonRoot }} onClick={handleOpen}>
<MenuIcon />
</IconButton>
<Menu
id="summary-menu"
anchorEl={menuAnchor}
open={Boolean(menuAnchor)}
onClose={handleClose}
getContentAnchorEl={null}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
>
<MenuItem onClick={locateFeature}>{t('map-ui:locate')}</MenuItem>
{doesExpire ? (
<MenuItem onClick={clearExpiredFeature}>{t('map-ui:clear-refreshed-markers')}</MenuItem>
) : null}
{displayed ? (
<MenuItem onClick={hideFeature}>{t('map-ui:hide-feature')}</MenuItem>
) : (
<MenuItem onClick={showFeature}>{t('map-ui:show-feature')}</MenuItem>
)}
<MenuItem onClick={clearAllFeature}>{t('map-ui:clear-all')}</MenuItem>
</Menu>
</>
);
}
Example #13
Source File: NewsMenu.tsx From The-TypeScript-Workshop with MIT License | 5 votes |
NewsMenu = () => {
const [anchorEl, setAnchorEl] = useState<Element>();
const history = useHistory();
const user = useContext(UserContext);
const handleClick = (
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => setAnchorEl(event.currentTarget);
const handleAdd = () => history.push('/add');
const handleClose = () => setAnchorEl(undefined);
const handleLogOut = async () => {
await auth.signOut();
history.push('/signin');
};
const handleSignIn = () => history.push('/signin');
const handleSignUp = () => history.push('/signup');
return (
<div>
<Button
area-controls="simple-menu"
area-haspopup="true"
color="inherit"
onClick={handleClick}
>
Menu
</Button>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
{user ? (
<div>
<MenuItem onClick={handleAdd}>Add a Story</MenuItem>
<MenuItem onClick={handleLogOut}>Log Out</MenuItem>
</div>
) : (
<div>
<MenuItem onClick={handleSignIn}>Sign In</MenuItem>
<MenuItem onClick={handleSignUp}>Sign Up</MenuItem>
</div>
)}
</Menu>
</div>
);
}
Example #14
Source File: UserMenu.tsx From knboard with MIT License | 5 votes |
UserMenu = () => {
const user = useSelector((state: RootState) => state.auth.user);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const dispatch = useDispatch();
const history = useHistory();
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleNotImplemented = () => {
dispatch(createInfoToast("Not implemented yet ?"));
};
const handleLogout = () => {
setAnchorEl(null);
dispatch(logout());
history.push("/");
};
const handleToProfile = () => {
setAnchorEl(null);
history.push("/profile");
};
return (
<>
<Button
aria-controls="user-menu"
aria-haspopup="true"
onClick={handleClick}
data-testid="user-menu"
css={css`
min-width: 1.5rem;
padding: 0;
border-radius: 50%;
&:hover {
background-color: initial;
}
`}
>
<Avatar
css={avatarStyles}
src={user?.photo_url || ""}
alt="user-avatar"
>
{user?.username.charAt(0)}
</Avatar>
</Button>
<Menu
id="user-menu"
anchorEl={anchorEl}
getContentAnchorEl={null}
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
transformOrigin={{ vertical: "top", horizontal: "center" }}
open={Boolean(anchorEl)}
onClose={handleClose}
transitionDuration={0}
keepMounted
>
<Username>{user?.username}</Username>
<MenuItem onClick={handleToProfile}>Profile</MenuItem>
<MenuItem onClick={handleNotImplemented}>Available Shortcuts</MenuItem>
<MenuItem onClick={handleLogout}>Logout</MenuItem>
</Menu>
</>
);
}
Example #15
Source File: icon_button_menu.spec.tsx From jupyter-extensions with Apache License 2.0 | 5 votes |
describe('IconButtonMenu', () => {
const menuItemsProp = (closeHandler: MenuCloseHandler) => (
<React.Fragment>
<MenuItem onClick={closeHandler}>Item 1</MenuItem>
<MenuItem onClick={closeHandler}>Item 2</MenuItem>
<MenuItem onClick={closeHandler}>Item 3</MenuItem>
</React.Fragment>
);
it('Renders menu items', () => {
const iconButtonMenu = shallow(
<IconButtonMenu menuItems={menuItemsProp} />
);
expect(iconButtonMenu).toMatchSnapshot();
});
it('Renders with provided icon', () => {
const iconButtonMenu = shallow(
<IconButtonMenu menuItems={menuItemsProp} icon={<Add />} />
);
expect(iconButtonMenu).toMatchSnapshot();
});
it('Opens from icon button and closes when an item is clicked', () => {
const iconButtonMenu = shallow(
<IconButtonMenu menuItems={menuItemsProp} />
);
expect(iconButtonMenu.find(Menu).prop('open')).toBe(false);
const openMenuButton = iconButtonMenu.find(IconButton).first();
openMenuButton.simulate('click', {
currentTarget: openMenuButton.getElement(),
});
expect(iconButtonMenu.find(Menu).prop('open')).toBe(true);
const menuItems = iconButtonMenu.find(MenuItem);
expect(menuItems.length).toBe(3);
menuItems.first().simulate('click');
expect(iconButtonMenu.find(Menu).prop('open')).toBe(false);
});
});
Example #16
Source File: ActionsMenu.tsx From max-todos with MIT License | 4 votes |
export default function ActionsMenu({
deleteTodo,
setEditOpen,
markStar,
todo,
}: Props) {
const [anchorEl, setAnchorEl] = useState<
(EventTarget & HTMLButtonElement) | null
>(null);
const open = Boolean(anchorEl);
const MenuIcon = useChangeMenuIcon();
const options: Option[] = [
{
name: todo.starred ? OptionName.UNSTAR : OptionName.STAR,
customColor: todo.starred ? "#CCA43A" : "#000",
icon: todo.starred ? StarIcon : StarIconOutlined,
method: () => {
markStar(todo.id);
setAnchorEl(null);
},
},
{
name: OptionName.EDIT,
iconColor: "primary",
textColor: "primary",
icon: EditIcon,
method: () => {
setEditOpen(true);
setAnchorEl(null);
},
},
{
name: OptionName.DELETE,
iconColor: "error",
textColor: "error",
icon: DeleteIcon,
method: (e) => {
deleteTodo(e);
setAnchorEl(null);
},
},
];
const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
setAnchorEl(e.currentTarget);
};
const handleEvent = (option: OptionName, e: any) => {
if (option === "Star") markStar(todo.id);
else if (option === "Edit") setEditOpen(true);
else if (option === "Delete") deleteTodo(e);
setAnchorEl(null);
};
return (
<div>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={handleClick}
centerRipple={false}
>
<MenuIcon />
</IconButton>
<Menu
id="long-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleEvent}
PaperProps={{
style: {
maxHeight: ITEM_HEIGHT * 4.5,
width: "20ch",
},
}}
>
{options.map((option) => (
<MenuItem key={option.name} onClick={option.method}>
<option.icon
color={option.iconColor}
htmlColor={option.customColor}
/>
<Typography
color={option.textColor}
style={{ color: option.customColor }}
>
{option.name}
</Typography>
</MenuItem>
))}
</Menu>
</div>
);
}
Example #17
Source File: Vulnerability.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 4 votes |
Vulnerability: React.FC = () => {
const { vulnerabilityId } = useParams();
const { apiGet, apiPut } = useAuthContext();
const [vulnerability, setVulnerability] = useState<VulnerabilityType>();
const [comment, setComment] = useState<string>('');
const [showCommentForm, setShowCommentForm] = useState<boolean>(false);
const [menuAnchor, setMenuAnchor] = React.useState<null | HTMLElement>(null);
const classes = useStyles();
const history = useHistory();
const formatDate = (date: string) => {
return format(parseISO(date), 'MM-dd-yyyy');
};
const fetchVulnerability = useCallback(async () => {
try {
const result = await apiGet<VulnerabilityType>(
`/vulnerabilities/${vulnerabilityId}`
);
setVulnerability(result);
} catch (e) {
console.error(e);
}
}, [vulnerabilityId, apiGet]);
const updateVulnerability = async (body: { [key: string]: string }) => {
try {
if (!vulnerability) return;
const res = await apiPut<VulnerabilityType>(
'/vulnerabilities/' + vulnerability.id,
{
body: body
}
);
setVulnerability({
...vulnerability,
state: res.state,
substate: res.substate,
actions: res.actions
});
} catch (e) {
console.error(e);
}
};
useEffect(() => {
fetchVulnerability();
}, [fetchVulnerability]);
if (!vulnerability) return <></>;
const references = vulnerability.references.map((ref) => ref);
if (vulnerability.cve)
references.unshift({
name: 'NIST National Vulnerability Database',
url: `https://nvd.nist.gov/vuln/detail/${vulnerability.cve}`,
source: '',
tags: []
});
const states = [
'unconfirmed',
'exploitable',
'false-positive',
'accepted-risk',
'remediated'
];
interface dnstwist {
'domain-name': string;
fuzzer: string;
'dns-a'?: string;
'dns-aaas'?: string;
'dns-mx'?: string;
'dns-ns'?: string;
'date-first-observed'?: string;
}
return (
<>
{/* <Alert severity="info">
This vulnerability is found on 17 domains you have access to.
</Alert> */}
<div className={classes.root}>
<p>
<Link
to="# "
onClick={() => history.goBack()}
className={classes.backLink}
>
<ChevronLeft
style={{
height: '100%',
verticalAlign: 'middle',
marginTop: '-2px'
}}
></ChevronLeft>
Go back
</Link>
</p>
<div className={classes.contentWrapper}>
<div className={classes.content}>
<div
className={classes.panel}
style={{
flex: '0 0 45%'
}}
>
<Paper classes={{ root: classes.cardRoot }}>
<div className={classes.title}>
<h4>{vulnerability.title}</h4>
<Button
aria-haspopup="true"
onClick={(event: React.MouseEvent<HTMLButtonElement>) =>
setMenuAnchor(event.currentTarget)
}
>
<Flag
style={{
fontSize: '14px',
color: '#A9AEB1',
marginRight: '5px'
}}
></Flag>
Mark Item <ArrowDropDown />
</Button>
<Menu
anchorEl={menuAnchor}
keepMounted
open={Boolean(menuAnchor)}
getContentAnchorEl={null}
onClose={() => setMenuAnchor(null)}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center'
}}
>
{states.map((state) => (
<MenuItem
key={state}
onClick={() => {
updateVulnerability({
substate: state
});
setMenuAnchor(null);
}}
style={{ outline: 'none' }}
>
{stateMap[state]}
</MenuItem>
))}
</Menu>
</div>
<Chip
style={{
marginLeft: '1.5rem'
}}
// icon={<Check></Check>}
label={`${vulnerability.state[0].toUpperCase()}${vulnerability.state.slice(
1
)} (${stateMap[vulnerability.substate]})`}
color={
vulnerability.state === 'open' ? 'secondary' : 'default'
}
/>
<div className={classes.inner}>
<div className={classes.section}>
<h4 className={classes.subtitle}>Description</h4>
{vulnerability.description}
</div>
<div className={classes.section}>
<h4 className={classes.subtitle}>References</h4>
{references &&
references.map((ref, index) => (
<p key={index}>
<a
href={ref.url}
target="_blank"
rel="noopener noreferrer"
>
{ref.name ? ref.name : ref.url}
</a>
{ref.tags.length > 0
? ' - ' + ref.tags.join(',')
: ''}
</p>
))}
</div>
{vulnerability.source === 'hibp' && (
<div className={classes.section}>
<h4 className={classes.subtitle}>Data</h4>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Exposed Emails</TableCell>
<TableCell align="right">Breaches</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.keys(
vulnerability.structuredData['emails']
).map((keyName, keyIndex) => (
<TableRow key={keyName}>
<TableCell component="th" scope="row">
{keyName}
</TableCell>
<TableCell align="right">
{vulnerability.structuredData['emails'][
keyName
].join(', ')}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}
{vulnerability.source === 'lookingGlass' && (
<div className={classes.section}>
<h4 className={classes.subtitle}>Data</h4>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>First Seen</TableCell>
<TableCell align="right">Last Seen</TableCell>
<TableCell align="right">Vuln Name</TableCell>
<TableCell align="right">Type</TableCell>
</TableRow>
</TableHead>
<TableBody>
{vulnerability.structuredData['lookingGlassData'].map(
(col: any) => (
<TableRow key={col.right_name}>
<TableCell component="th" scope="row">
{formatDistanceToNow(
parseISO(col.firstSeen)
) + ' ago'}
</TableCell>
<TableCell align="right">
{formatDistanceToNow(parseISO(col.lastSeen)) +
' ago'}
</TableCell>
<TableCell align="right">
{col.right_name}
</TableCell>
<TableCell align="right">
{col.vulnOrMal}
</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</div>
)}
{vulnerability.source === 'dnstwist' && (
<div className={classes.section}>
<h4 className={classes.subtitle}>Data</h4>
<TableContainer>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Domain Name</TableCell>
<TableCell>IP Address / A Record</TableCell>
<TableCell>MX Record</TableCell>
<TableCell>NS Record</TableCell>
<TableCell>Date Observed</TableCell>
<TableCell>Fuzzer</TableCell>
</TableRow>
</TableHead>
<TableBody>
{vulnerability.structuredData['domains'].map(
(dom: dnstwist) => (
<TableRow key={dom['domain-name']}>
<TableCell component="th" scope="row">
{dom['domain-name']}
</TableCell>
<TableCell>{dom['dns-a']}</TableCell>
<TableCell>{dom['dns-mx']}</TableCell>
<TableCell>{dom['dns-ns']}</TableCell>
<TableCell>
{dom['date-first-observed']}
</TableCell>
<TableCell>{dom['fuzzer']}</TableCell>
</TableRow>
)
)}
</TableBody>
</Table>
</TableContainer>
</div>
)}
</div>
</Paper>
</div>
<div
className={classes.panel}
style={{
flex: '0 0 30%'
}}
>
<Paper className={classes.cardRoot}>
<div className={classes.inner}>
<div className={classes.section}>
<h2 className={classes.subtitle}>Team notes</h2>
<button
onClick={() => {
setShowCommentForm(!showCommentForm);
}}
className={classes.linkSmall}
>
Add new note
</button>
</div>
{showCommentForm && (
<div>
<TextareaAutosize
style={{
width: '100%',
padding: 10,
marginBottom: '20px'
}}
rowsMin={4}
placeholder="Leave a Note"
onChange={(e) => setComment(e.target.value)}
/>
<Button
onClick={() => {
updateVulnerability({
comment
});
setComment('');
setShowCommentForm(false);
}}
style={{
width: 150,
marginBottom: '20px'
}}
variant="contained"
color="secondary"
>
Save
</Button>
</div>
)}
{vulnerability.actions &&
vulnerability.actions
.filter((action) => action.type === 'comment')
.map((action, index) => (
<div className={classes.section} key={index}>
<h4
className={classes.subtitle}
style={{ fontSize: '16px', display: 'inline' }}
>
{action.userName}
</h4>
<span style={{ float: 'right', display: 'inline' }}>
{formatDistanceToNow(parseISO(action.date))} ago
</span>
<ReactMarkdown linkTarget="_blank">
{action.value || ''}
</ReactMarkdown>
</div>
))}
</div>
</Paper>
<Paper className={classes.cardRoot}>
<div className={classes.inner}>
<div className={classes.section}>
<h2 className={classes.subtitle}>Vulnerability History</h2>
</div>
<Timeline
style={{
position: 'relative',
marginLeft: '-90%'
}}
align="left"
>
{vulnerability.actions &&
vulnerability.actions
.filter(
(action) =>
action.type === 'state-change' && action.substate
)
.map((action, index) => (
<TimelineItem key={index}>
<TimelineSeparator>
<TimelineDot />
<TimelineConnector />
</TimelineSeparator>{' '}
<TimelineContent>
State {action.automatic ? 'automatically ' : ''}
changed to {action.state} (
{stateMap[action.substate!].toLowerCase()})
{action.userName ? ' by ' + action.userName : ''}{' '}
<br></br>
<span
style={{
color: '#A9AEB1'
}}
>
{formatDate(action.date)}
</span>
</TimelineContent>
</TimelineItem>
))}
<TimelineItem>
<TimelineSeparator>
<TimelineDot />
</TimelineSeparator>
<TimelineContent>
Vulnerability opened<br></br>
<span
style={{
color: '#A9AEB1'
}}
>
{formatDate(vulnerability.createdAt)}
</span>
</TimelineContent>
</TimelineItem>
</Timeline>
</div>
</Paper>
<Paper className={classes.cardRoot}>
<div className={classes.inner}>
<div className={classes.section}>
<h2 className={classes.subtitle}>Provenance</h2>
<p>
<strong>Root Domain: </strong>
{vulnerability.domain.fromRootDomain}
</p>
<p>
<strong>Subdomain: </strong>
{vulnerability.domain.name} (
{vulnerability.domain.subdomainSource})
</p>
{vulnerability.service && (
<p>
<strong>Service/Port: </strong>
{vulnerability.service.service
? vulnerability.service.service
: vulnerability.service.port}{' '}
({vulnerability.service.serviceSource})
</p>
)}
{vulnerability.cpe && (
<>
<p>
<strong>Product: </strong>
{vulnerability.cpe}
</p>
</>
)}
<p>
<strong>Vulnerability: </strong>
{vulnerability.title} ({vulnerability.source})
</p>
</div>
</div>
</Paper>
{vulnerability.source === 'hibp' && (
<Paper className={classes.cardRoot}>
<div className={classes.inner}>
<div className={classes.section}>
<h2 className={classes.subtitle}>Breaches</h2>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Breach Name</TableCell>
<TableCell align="right">Date Added</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.keys(vulnerability.structuredData['breaches'])
.sort(
(a, b) =>
parseISO(
vulnerability.structuredData['breaches'][b][
'AddedDate'
]
).getTime() -
parseISO(
vulnerability.structuredData['breaches'][a][
'AddedDate'
]
).getTime()
)
.map((keyName, keyIndex) => (
<TableRow key={keyName}>
<TableCell component="th" scope="row">
{keyName}
</TableCell>
<TableCell align="right">
{formatDistanceToNow(
parseISO(
vulnerability.structuredData['breaches'][
keyName
]['AddedDate']
)
) + ' ago'}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
</Paper>
)}
</div>
</div>
</div>
</div>
</>
);
}
Example #18
Source File: NavItem.tsx From crossfeed with Creative Commons Zero v1.0 Universal | 4 votes |
NavItem: React.FC<Props> = (props) => {
const { title, path, nested, exact, onClick } = props;
const match = useRouteMatch(path ?? '');
const history = useHistory();
const [anchor, setAnchor] = useState<any>(null);
const [mouseInButton, setMouseInButton] = useState(false);
const [mouseInMenu, setMouseInMenu] = useState(false);
const classes = useStyles();
const onClickButton = (e: any) => {
setAnchor(e.currentTarget);
setMouseInButton(true);
};
const onCloseMenu = () => {
setMouseInMenu(false);
setMouseInButton(false);
setAnchor(null);
};
const navigateTo = (to: string) => {
setMouseInMenu(false);
setMouseInButton(false);
setAnchor(null);
history.push(to);
};
return (
<>
{path ? (
<NavLink
to={path}
activeClassName={path !== '#' ? classes.activeLink : classes.link}
className={classes.link}
onClick={onClick ? onClick : onClickButton}
exact={exact}
style={{ outline: 'none' }}
>
{title}
</NavLink>
) : (
<Button
className={clsx(classes.link, {
[classes.activeLink]: !!match
})}
onClick={onClick ? onClick : onClickButton}
style={{ outline: 'none' }}
>
{title}
</Button>
)}
{nested && (
<Menu
id={`menu-${title}`}
open={(mouseInButton || mouseInMenu) && !!anchor}
anchorEl={anchor}
onClose={onCloseMenu}
getContentAnchorEl={null}
keepMounted
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center'
}}
>
{nested.map((item) => (
<MenuItem
key={item.title.toString()}
onClick={
item.onClick ? item.onClick : () => navigateTo(item.path)
}
style={{ outline: 'none' }}
>
{item.title}
</MenuItem>
))}
</Menu>
)}
</>
);
}
Example #19
Source File: MoreMenu.tsx From max-todos with MIT License | 4 votes |
MoreMenu = () => {
const [anchorEl, setAnchorEl] = useState<
(EventTarget & HTMLButtonElement) | null
>(null);
const [deleteOpen, setDeleteOpen] = useState(false);
const open = Boolean(anchorEl);
const MenuIcon = useChangeMenuIcon();
const { todos, deleteAll } = useContext(MainContext)!;
const handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) =>
setAnchorEl(e.currentTarget);
const handleClose = () => setAnchorEl(null);
interface Option {
name: string;
iconColor:
| "error"
| "action"
| "inherit"
| "disabled"
| "primary"
| "secondary"
| undefined;
textColor:
| "error"
| "inherit"
| "primary"
| "secondary"
| "initial"
| "textPrimary"
| "textSecondary"
| undefined;
disabled: boolean;
icon: SvgIconComponent;
method: () => void;
}
const options: Option[] = [
{
name: "Delete All",
iconColor: "error",
textColor: "error",
disabled: todos.length === 0,
icon: DeleteSweepIcon,
method: () => {
handleClose();
setDeleteOpen(true);
},
},
];
return (
<div>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={handleClick}
color="inherit"
>
<MenuIcon />
</IconButton>
<Menu
id="long-menu"
anchorEl={anchorEl}
keepMounted
open={open}
onClose={handleClose}
PaperProps={{
style: {
width: "20ch",
},
}}
>
{options.map((option) => (
<MenuItem
key={option.name}
disabled={option.disabled}
onClick={option.method}
>
<option.icon color={option.iconColor} />
<Typography color={option.textColor}>{option.name}</Typography>
</MenuItem>
))}
</Menu>
<DeleteAllConfirm
yes={() => {
setDeleteOpen(false);
setTimeout(() => deleteAll(), 200);
}}
open={deleteOpen}
close={() => setDeleteOpen(false)}
/>
</div>
);
}
Example #20
Source File: Menu.tsx From signer with Apache License 2.0 | 4 votes |
MoreMenu = observer((props: Props) => {
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const classes = useStyles();
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setTimeout(() => {
setAnchorEl(null);
}, 200);
};
return (
<div>
<IconButton
edge="end"
aria-controls="simple-menu"
aria-haspopup="true"
onClick={handleClick}
style={{ color: '#C4C4C4' }}
>
<MenuIcon />
</IconButton>
<Menu
id={'simple-menu'}
anchorEl={anchorEl}
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
open={Boolean(anchorEl)}
onClose={handleClose}
>
<List
aria-labelledby="nested-list-subheader"
subheader={
<ListSubheader component="div" id="nested-list-subheader">
{props.accountManager.userAccounts.length > 0
? 'Accounts'
: 'No Account'}
</ListSubheader>
}
>
{props.accountManager.userAccounts.map((account, i) => {
return (
<ListItem
key={i}
button
dense={true}
onClick={() => {
props.accountManager.switchToAccount(account.alias);
handleClose();
}}
>
{account.alias ===
props.accountManager.activeUserAccount?.alias ? (
<CheckIcon fontSize={'small'} />
) : (
<Icon className={'fa fa-fw'} fontSize={'small'} />
)}
<ListItemText primary={account.alias} />
</ListItem>
);
})}
<Divider light />
{props.accountManager.userAccounts.length > 0 && (
<ListItem
dense={true}
component={Link}
to={Pages.AccountManagement}
button
onClick={handleClose}
>
<SettingsIcon className={classes.menuIcon} />
<ListItemText primary="Key Management" />
</ListItem>
)}
<ListItem
dense={true}
component={Link}
to={Pages.ConnectedSites}
button
onClick={handleClose}
>
<WebIcon className={classes.menuIcon} />
<ListItemText primary="Connected Sites" />
</ListItem>
{props.accountManager.activeUserAccount && (
<ListItem
dense={true}
button
onClick={() => {
props.accountManager.downloadActiveKey();
handleClose();
}}
>
<CloudDownloadIcon className={classes.menuIcon} />
<ListItemText primary="Download Active Key" />
</ListItem>
)}
<ListItem
dense={true}
component={Link}
to={Pages.ConfigureTimeout}
button
onClick={handleClose}
>
<TimerIcon className={classes.menuIcon} />
<ListItemText primary="Timeout" />
<Typography variant="overline">
{props.accountManager.idleTimeoutMins} min
{props.accountManager.idleTimeoutMins === 1 ? '' : 's'}
</Typography>
</ListItem>
<ListItem
dense={true}
button
onClick={() => {
props.accountManager.lock();
handleClose();
}}
>
<LockIcon className={classes.menuIcon} />
<ListItemText primary="Lock" />
</ListItem>
</List>
</Menu>
</div>
);
})
Example #21
Source File: index.tsx From aqualink-app with MIT License | 4 votes |
NavBar = ({
searchLocation,
geocodingEnabled,
routeButtons,
loading,
classes,
}: NavBarProps) => {
const user = useSelector(userInfoSelector);
const storedCollection = useSelector(collectionDetailsSelector);
const dispatch = useDispatch();
const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.up("md"));
const [registerDialogOpen, setRegisterDialogOpen] = useState<boolean>(false);
const [signInDialogOpen, setSignInDialogOpen] = useState<boolean>(false);
const [menuDrawerOpen, setMenuDrawerOpen] = useState<boolean>(false);
const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);
const handleRegisterDialog = (open: boolean) => setRegisterDialogOpen(open);
const handleSignInDialog = (open: boolean) => setSignInDialogOpen(open);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleMenuClose = () => {
setAnchorEl(null);
};
const onUserSignOut = () => {
// Clear collection if it belongs to the signed in user
if (storedCollection?.id === user?.collection?.id) {
dispatch(clearCollection());
}
dispatch(signOutUser());
handleMenuClose();
};
const onSiteChange = () => {
dispatch(unsetSelectedSite());
dispatch(unsetLiveData());
dispatch(unsetLatestData());
};
return (
<>
<AppBar
className={classNames(classes.appBar, {
[classes.appBarXs]: searchLocation,
})}
position="static"
color="primary"
>
<Toolbar className={classes.toolbar}>
<MenuDrawer
open={menuDrawerOpen}
onClose={() => setMenuDrawerOpen(false)}
/>
<Grid
container
justify="space-between"
alignItems="center"
spacing={1}
>
<Grid
item
xs={5}
sm={2}
// eslint-disable-next-line no-nested-ternary
md={routeButtons ? 2 : searchLocation ? 6 : 4}
>
<Box display="flex" flexWrap="nowrap" alignItems="center">
<IconButton
edge="start"
color="inherit"
onClick={() => setMenuDrawerOpen(true)}
>
<MenuIcon />
</IconButton>
<MuiLink className={classes.navBarLink} href="/map">
<Typography color="textPrimary" variant="h4">
Aqua
</Typography>
<Typography style={{ color: "#8AC6DE" }} variant="h4">
link
</Typography>
</MuiLink>
</Box>
</Grid>
{searchLocation && (
<Hidden xsDown>
<Grid item sm={4} md={3}>
<Search geocodingEnabled={geocodingEnabled} />
</Grid>
</Hidden>
)}
{routeButtons && isTablet && <RouteButtons />}
<Grid
container
justify="flex-end"
item
xs={7}
sm={routeButtons && isTablet ? 3 : 4}
md={searchLocation || (routeButtons && isTablet) ? 3 : 8}
>
{user ? (
<>
<Box display="flex" flexWrap="nowrap" alignItems="center">
{user.fullName ? user.fullName : "My Profile"}
<IconButton
className={classes.button}
onClick={handleClick}
>
<ExpandMoreIcon className={classes.expandIcon} />
</IconButton>
<Menu
key="user-menu"
className={classes.userMenu}
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleMenuClose}
MenuListProps={{ className: classes.userMenu }}
PopoverClasses={{ paper: classes.userMenuWrapper }}
>
{sortBy(user.administeredSites, "id").map(
({ id, name, region }, index) => {
const siteIdentifier = name || region;
return (
<Link
to={`/sites/${id}`}
key={`site-link-${id}`}
className={classes.menuItemLink}
>
<MenuItem
onClick={() => onSiteChange()}
className={classes.menuItem}
>
{siteIdentifier || `Site ${index + 1}`}
</MenuItem>
</Link>
);
}
)}
<Divider className={classes.userMenuDivider} />
<Link to="/dashboard" className={classes.menuItemLink}>
<MenuItem
key="user-menu-dashboard"
className={classes.menuItem}
>
<Grid container spacing={1}>
<Grid item>
<DashboardTwoToneIcon fontSize="small" />
</Grid>
<Grid item>Dashboard</Grid>
</Grid>
</MenuItem>
</Link>
<Divider className={classes.userMenuDivider} />
<MenuItem
key="user-menu-logout"
className={classes.menuItem}
onClick={onUserSignOut}
>
<Grid container spacing={1}>
<Grid item>
<PowerSettingsNewIcon fontSize="small" />
</Grid>
<Grid item>Logout</Grid>
</Grid>
</MenuItem>
</Menu>
</Box>
</>
) : (
<>
<Grid item>
<Button onClick={() => handleSignInDialog(true)}>
SIGN IN
</Button>
</Grid>
<Grid item>
<Button onClick={() => handleRegisterDialog(true)}>
SIGN UP
</Button>
</Grid>
</>
)}
</Grid>
{searchLocation && (
<Hidden smUp>
<Grid item xs={12} style={{ margin: 0, paddingTop: 0 }}>
<Search geocodingEnabled={geocodingEnabled} />
</Grid>
</Hidden>
)}
</Grid>
</Toolbar>
</AppBar>
{loading && <LinearProgress />}
<RegisterDialog
open={registerDialogOpen}
handleRegisterOpen={handleRegisterDialog}
handleSignInOpen={handleSignInDialog}
/>
<SignInDialog
open={signInDialogOpen}
handleRegisterOpen={handleRegisterDialog}
handleSignInOpen={handleSignInDialog}
/>
</>
);
}
Example #22
Source File: UptimeMonitorActionsMenu.tsx From backstage with Apache License 2.0 | 4 votes |
UptimeMonitorActionsMenu = ({
uptimeMonitor,
onUptimeMonitorChanged,
}: {
uptimeMonitor: UptimeMonitor;
onUptimeMonitorChanged?: (uptimeMonitor: UptimeMonitor) => void;
}) => {
const ilertApi = useApi(ilertApiRef);
const alertApi = useApi(alertApiRef);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const callback = onUptimeMonitorChanged || ((_: UptimeMonitor): void => {});
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleCloseMenu = () => {
setAnchorEl(null);
};
const handlePause = async (): Promise<void> => {
try {
const newUptimeMonitor = await ilertApi.pauseUptimeMonitor(uptimeMonitor);
handleCloseMenu();
alertApi.post({ message: 'Uptime monitor paused.' });
callback(newUptimeMonitor);
} catch (err) {
alertApi.post({ message: err, severity: 'error' });
}
};
const handleResume = async (): Promise<void> => {
try {
const newUptimeMonitor = await ilertApi.resumeUptimeMonitor(
uptimeMonitor,
);
handleCloseMenu();
alertApi.post({ message: 'Uptime monitor resumed.' });
callback(newUptimeMonitor);
} catch (err) {
alertApi.post({ message: err, severity: 'error' });
}
};
const handleOpenReport = async (): Promise<void> => {
try {
const um = await ilertApi.fetchUptimeMonitor(uptimeMonitor.id);
handleCloseMenu();
window.open(um.shareUrl, '_blank');
} catch (err) {
alertApi.post({ message: err, severity: 'error' });
}
};
return (
<>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={handleClick}
size="small"
>
<MoreVertIcon />
</IconButton>
<Menu
id={`uptime-monitor-actions-menu-${uptimeMonitor.id}`}
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleCloseMenu}
PaperProps={{
style: { maxHeight: 48 * 4.5 },
}}
>
{uptimeMonitor.paused ? (
<MenuItem key="ack" onClick={handleResume}>
<Typography variant="inherit" noWrap>
Resume
</Typography>
</MenuItem>
) : null}
{!uptimeMonitor.paused ? (
<MenuItem key="close" onClick={handlePause}>
<Typography variant="inherit" noWrap>
Pause
</Typography>
</MenuItem>
) : null}
<MenuItem key="report" onClick={handleCloseMenu}>
<Typography variant="inherit" noWrap>
<Link to="#" onClick={handleOpenReport}>
View Report
</Link>
</Typography>
</MenuItem>
<MenuItem key="details" onClick={handleCloseMenu}>
<Typography variant="inherit" noWrap>
<Link to={ilertApi.getUptimeMonitorDetailsURL(uptimeMonitor)}>
View in iLert
</Link>
</Typography>
</MenuItem>
</Menu>
</>
);
}
Example #23
Source File: IncidentActionsMenu.tsx From backstage with Apache License 2.0 | 4 votes |
IncidentActionsMenu = ({
incident,
onIncidentChanged,
setIsLoading,
}: {
incident: Incident;
onIncidentChanged?: (incident: Incident) => void;
setIsLoading?: (isLoading: boolean) => void;
}) => {
const ilertApi = useApi(ilertApiRef);
const alertApi = useApi(alertApiRef);
const identityApi = useApi(identityApiRef);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const callback = onIncidentChanged || ((_: Incident): void => {});
const setProcessing = setIsLoading || ((_: boolean): void => {});
const [isAssignIncidentModalOpened, setIsAssignIncidentModalOpened] =
React.useState(false);
const [{ incidentActions, isLoading }] = useIncidentActions(
incident,
Boolean(anchorEl),
);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleCloseMenu = () => {
setAnchorEl(null);
};
const handleAccept = async (): Promise<void> => {
try {
handleCloseMenu();
setProcessing(true);
const { userEntityRef } = await identityApi.getBackstageIdentity();
const { name: userName } = parseEntityRef(userEntityRef, {
defaultKind: 'User',
defaultNamespace: DEFAULT_NAMESPACE,
});
const newIncident = await ilertApi.acceptIncident(incident, userName);
alertApi.post({ message: 'Incident accepted.' });
callback(newIncident);
setProcessing(false);
} catch (err) {
setProcessing(false);
alertApi.post({ message: err, severity: 'error' });
}
};
const handleResolve = async (): Promise<void> => {
try {
handleCloseMenu();
setProcessing(true);
const { userEntityRef } = await identityApi.getBackstageIdentity();
const { name: userName } = parseEntityRef(userEntityRef, {
defaultKind: 'User',
defaultNamespace: DEFAULT_NAMESPACE,
});
const newIncident = await ilertApi.resolveIncident(incident, userName);
alertApi.post({ message: 'Incident resolved.' });
callback(newIncident);
setProcessing(false);
} catch (err) {
setProcessing(false);
alertApi.post({ message: err, severity: 'error' });
}
};
const handleAssign = () => {
handleCloseMenu();
setIsAssignIncidentModalOpened(true);
};
const handleTriggerAction = (action: IncidentAction) => async () => {
try {
handleCloseMenu();
setProcessing(true);
await ilertApi.triggerIncidentAction(incident, action);
alertApi.post({ message: 'Incident action triggered.' });
setProcessing(false);
} catch (err) {
setProcessing(false);
alertApi.post({ message: err, severity: 'error' });
}
};
const actions: React.ReactNode[] = incidentActions.map(a => {
const successTrigger = a.history
? a.history.find(h => h.success)
: undefined;
const triggeredBy =
successTrigger && successTrigger.actor
? `${successTrigger.actor.firstName} ${successTrigger.actor.lastName}`
: '';
return (
<MenuItem
key={a.webhookId}
onClick={handleTriggerAction(a)}
disabled={!!successTrigger}
>
<Typography variant="inherit" noWrap>
{triggeredBy ? `${a.name} (by ${triggeredBy})` : a.name}
</Typography>
</MenuItem>
);
});
return (
<>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
onClick={handleClick}
size="small"
>
<MoreVertIcon />
</IconButton>
<Menu
id={`incident-actions-menu-${incident.id}`}
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleCloseMenu}
PaperProps={{
style: { maxHeight: 48 * 5.5 },
}}
>
{incident.status === 'PENDING' ? (
<MenuItem key="ack" onClick={handleAccept}>
<Typography variant="inherit" noWrap>
Accept
</Typography>
</MenuItem>
) : null}
{incident.status !== 'RESOLVED' ? (
<MenuItem key="close" onClick={handleResolve}>
<Typography variant="inherit" noWrap>
Resolve
</Typography>
</MenuItem>
) : null}
{incident.status !== 'RESOLVED' ? (
<MenuItem key="assign" onClick={handleAssign}>
<Typography variant="inherit" noWrap>
Assign
</Typography>
</MenuItem>
) : null}
{isLoading ? (
<MenuItem key="loading">
<Progress style={{ width: '100%' }} />
</MenuItem>
) : (
actions
)}
<MenuItem key="details" onClick={handleCloseMenu}>
<Typography variant="inherit" noWrap>
<Link to={ilertApi.getIncidentDetailsURL(incident)}>
View in iLert
</Link>
</Typography>
</MenuItem>
</Menu>
<IncidentAssignModal
incident={incident}
setIsModalOpened={setIsAssignIncidentModalOpened}
isModalOpened={isAssignIncidentModalOpened}
onIncidentChanged={onIncidentChanged}
/>
</>
);
}
Example #24
Source File: CostInsightsTabs.tsx From backstage with Apache License 2.0 | 4 votes |
CostInsightsTabs = ({ groups }: CostInsightsTabsProps) => {
const classes = useStyles();
const [index] = useState(0); // index is fixed for now until other tabs are added
const [groupMenuEl, setGroupMenuEl] = useState<Element | null>(null);
const { group, setGroup } = useFilters(mapFiltersToProps);
const { loadingActions, dispatchReset } = useLoading(mapLoadingToProps);
const openGroupMenu = (e: any) => setGroupMenuEl(e.currentTarget as Element);
const closeGroupMenu = () => setGroupMenuEl(null);
const updateGroupFilterAndCloseMenu = (g: Group) => () => {
dispatchReset(loadingActions);
closeGroupMenu();
setGroup(g);
};
const renderTabLabel = () => (
<div className={classes.tabLabel}>
<Typography className={classes.tabLabelText} variant="overline">
{`${groups.length} teams`}
</Typography>
<ExpandMoreIcon fontSize="small" />
</div>
);
const hasAtLeastTwoGroups = groups.length >= 2;
if (!hasAtLeastTwoGroups) return null;
return (
<>
<Tabs
className={`cost-insights-tabs ${classes.tabs}`}
data-testid="cost-insights-tabs"
classes={{ indicator: classes.indicator }}
value={index}
>
<Tab
className={classes.tab}
data-testid="cost-insights-groups-tab"
key="cost-insights-groups-tab"
label={renderTabLabel()}
onClick={openGroupMenu}
component="button"
/>
</Tabs>
<Menu
id="group-menu"
data-testid="group-menu"
className={classes.menu}
getContentAnchorEl={null}
anchorEl={groupMenuEl}
keepMounted
open={Boolean(groupMenuEl)}
onClose={closeGroupMenu}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
>
{groups.map((g: Group) => (
<MenuItem
className={classes.menuItem}
classes={{ selected: classes.menuItemSelected }}
selected={g.id === group}
key={g.id}
data-testid={g.id}
onClick={updateGroupFilterAndCloseMenu(g)}
>
{g.id}
</MenuItem>
))}
</Menu>
</>
);
}
Example #25
Source File: SidebarThemeSwitcher.tsx From backstage with Apache License 2.0 | 4 votes |
SidebarThemeSwitcher = () => {
const appThemeApi = useApi(appThemeApiRef);
const themeId = useObservable(
appThemeApi.activeThemeId$(),
appThemeApi.getActiveThemeId(),
);
const themeIds = appThemeApi.getInstalledThemes();
const activeTheme = themeIds.find(t => t.id === themeId);
const [anchorEl, setAnchorEl] = useState<Element | undefined>();
const open = Boolean(anchorEl);
const handleOpen = (event: React.MouseEvent) => {
setAnchorEl(event.currentTarget);
};
const handleSelectTheme = (newThemeId: string | undefined) => {
if (themeIds.some(t => t.id === newThemeId)) {
appThemeApi.setActiveThemeId(newThemeId);
} else {
appThemeApi.setActiveThemeId(undefined);
}
setAnchorEl(undefined);
};
const handleClose = () => {
setAnchorEl(undefined);
};
const ActiveIcon = useCallback(
() => <ThemeIcon icon={activeTheme?.icon} />,
[activeTheme],
);
return (
<>
<SidebarItem
icon={ActiveIcon}
text="Switch Theme"
id="theme-button"
aria-haspopup="listbox"
aria-controls="theme-menu"
aria-label="switch theme"
aria-expanded={open ? 'true' : undefined}
onClick={handleOpen}
/>
<Menu
id="theme-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'theme-button',
role: 'listbox',
}}
>
<MenuItem disabled>Choose a theme</MenuItem>
<MenuItem
selected={themeId === undefined}
onClick={() => handleSelectTheme(undefined)}
>
<ListItemIcon>
<ThemeIcon icon={undefined} active={themeId === undefined} />
</ListItemIcon>
<ListItemText>Auto</ListItemText>
</MenuItem>
{themeIds.map(theme => {
const active = theme.id === themeId;
return (
<MenuItem
key={theme.id}
selected={active}
aria-selected={active}
onClick={() => handleSelectTheme(theme.id)}
>
<ListItemIcon>
<ThemeIcon icon={theme.icon} active={active} />
</ListItemIcon>
<ListItemText>{theme.title}</ListItemText>
</MenuItem>
);
})}
</Menu>
</>
);
}
Example #26
Source File: index.tsx From react-app-architecture with Apache License 2.0 | 4 votes |
export default function Header(): ReactElement {
const classes = useStyles();
const history = useHistory();
const { isLoggedIn, data: authData } = useStateSelector(({ authState }) => authState);
const user = authData?.user;
const isWriter = checkRole(user, Roles.WRITER);
const isEditor = checkRole(user, Roles.EDITOR);
const [openAuthDialog, setOpenAuthDialog] = useState(false);
const [drawerOpen, setDrawerOpen] = useState(false);
const [popupMoreAnchorEl, setPopupMoreAnchorEl] = useState<HTMLElement | null>(null);
const isPopupMenuOpen = Boolean(popupMoreAnchorEl);
const dispatch = useDispatch();
function handlePopupMenuClose() {
setPopupMoreAnchorEl(null);
}
function handlePopupMenuOpen(event: MouseEvent<HTMLElement>) {
setPopupMoreAnchorEl(event.currentTarget);
}
function toggleDrawer() {
setDrawerOpen(!drawerOpen);
}
const renderProfileView = (onClick: (event: MouseEvent<HTMLButtonElement>) => void) => {
if (!user) return null;
return (
<CardActionArea onClick={onClick}>
{user.profilePicUrl ? (
<CardHeader
title={user.name.split(' ')[0]}
avatar={
<Avatar className={classes.avatar} aria-label={user.name} src={user.profilePicUrl} />
}
/>
) : (
<CardHeader title={user.name.split(' ')[0]} avatar={<FirstLetter text={user.name} />} />
)}
</CardActionArea>
);
};
const popupMenuId = 'menu-popup';
const popupMenu = (
<Menu
anchorEl={popupMoreAnchorEl}
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
id={popupMenuId}
keepMounted
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
open={isPopupMenuOpen}
onClose={handlePopupMenuClose}
PopoverClasses={{ paper: classes.paper }}
>
{isLoggedIn && renderProfileView(handlePopupMenuClose)}
{isWriter && (
<MenuItem
className={classes.menuItem}
onClick={() => {
history.push('/write/blog');
handlePopupMenuClose();
}}
>
<IconButton color="inherit">
<CreateIcon />
</IconButton>
<p>Write Blog</p>
</MenuItem>
)}
{isWriter && (
<MenuItem
className={classes.menuItem}
onClick={() => {
history.push('/writer/blogs');
handlePopupMenuClose();
}}
>
<IconButton color="inherit">
<ListIcon />
</IconButton>
<p>My Blogs</p>
</MenuItem>
)}
{isEditor && (
<MenuItem
className={classes.menuItem}
onClick={() => {
history.push('/editor/blogs');
handlePopupMenuClose();
}}
>
<IconButton color="inherit">
<SupervisorAccountIcon />
</IconButton>
<p>Blogs Admin</p>
</MenuItem>
)}
{isLoggedIn && (
<MenuItem
className={classes.menuItem}
onClick={() => {
dispatch(logout());
handlePopupMenuClose();
}}
>
<IconButton color="inherit">
<SvgIcon>
<path d={mdiLogout} />
</SvgIcon>
</IconButton>
<p>Logout</p>
</MenuItem>
)}
</Menu>
);
const mobileDrawerMenu = (
<Drawer anchor="top" open={drawerOpen} onClose={toggleDrawer}>
{isLoggedIn && renderProfileView(toggleDrawer)}
<List component="nav">
{[
{
title: 'About Project',
href: 'https://github.com/afteracademy/react-app-architecture',
icon: <InfoIcon />,
},
{
title: 'Contact',
href: 'https://github.com/afteracademy/react-app-architecture/issues',
icon: <EmailIcon />,
},
].map(({ title, href, icon }, position) => (
<ListItem
key={position}
className={classes.drawerItem}
button
href={href}
target="_blank"
onClick={toggleDrawer}
component="a"
>
<ListItemIcon className={classes.drawerIcon}>{icon}</ListItemIcon>
<ListItemText primary={title} />
</ListItem>
))}
{[{ title: 'Blogs', link: '/blogs', icon: <WebIcon /> }].map(
({ title, link, icon }, position) => (
<ListItem
key={position}
className={classes.drawerItem}
button
component={Link}
to={link}
onClick={toggleDrawer}
>
<ListItemIcon className={classes.drawerIcon}>{icon}</ListItemIcon>
<ListItemText primary={title} />
</ListItem>
),
)}
{isWriter && <Divider />}
{isWriter &&
[
{ title: 'Write Blog', link: '/write/blog', icon: <CreateIcon /> },
{ title: 'My Blogs', link: '/writer/blogs', icon: <WebIcon /> },
].map(({ title, link, icon }, position) => (
<ListItem
key={position}
className={classes.drawerItem}
button
component={Link}
to={link}
onClick={toggleDrawer}
>
<ListItemIcon className={classes.drawerIcon}>{icon}</ListItemIcon>
<ListItemText primary={title} />
</ListItem>
))}
<Divider />
{isEditor && <Divider />}
{isEditor &&
[{ title: 'Blog Admin', link: '/editor/blogs', icon: <SupervisorAccountIcon /> }].map(
({ title, link, icon }, position) => (
<ListItem
key={position}
className={classes.drawerItem}
button
component={Link}
to={link}
onClick={toggleDrawer}
>
<ListItemIcon className={classes.drawerIcon}>{icon}</ListItemIcon>
<ListItemText primary={title} />
</ListItem>
),
)}
{isLoggedIn && (
<ListItem
className={classes.drawerItem}
onClick={() => {
dispatch(logout());
toggleDrawer();
}}
button
>
<ListItemIcon className={classes.drawerIcon}>
<SvgIcon>
<path d={mdiLogout} />
</SvgIcon>
</ListItemIcon>
<ListItemText primary="Logout" />
</ListItem>
)}
{!isLoggedIn && (
<ListItem
className={classes.drawerItem}
onClick={() => {
setOpenAuthDialog(true);
toggleDrawer();
}}
button
>
<ListItemIcon className={classes.drawerIcon}>
<SvgIcon>
<path d={mdiLogin} />
</SvgIcon>
</ListItemIcon>
<ListItemText primary="Login" />
</ListItem>
)}
</List>
<div className={classes.drawerCloseButtonContainer}>
<IconButton className={classes.drawerCloseButton} onClick={toggleDrawer}>
<CloseIcon />
</IconButton>
</div>
</Drawer>
);
return (
<div className={classes.root}>
<AppBar position="fixed" color="secondary" className={classes.appbar}>
<Toolbar>
<Avatar
alt="Logo"
src={afterAcademyLogo}
className={classes.logo}
component={Link}
to={'/'}
/>
<Typography variant="h6" className={classes.brandName}>
AfterAcademy React
</Typography>
<div className={classes.sectionDesktop}>
{[
{
title: 'About Project',
href: 'https://github.com/afteracademy/react-app-architecture',
},
{
title: 'Contact',
href: 'https://github.com/afteracademy/react-app-architecture/issues',
},
].map(({ title, href }, position) => (
<Button
key={position}
color="inherit"
className={classes.button}
href={href}
target="_blank"
>
{title}
</Button>
))}
{[{ title: 'Blogs', link: '/blogs' }].map(({ title, link }, position) => (
<Button
key={position}
color="inherit"
className={classes.button}
component={Link}
to={link}
>
{title}
</Button>
))}
{user?.profilePicUrl ? (
<Avatar alt={user.name} src={user.profilePicUrl} className={classes.avatar} />
) : (
user?.name && <FirstLetter text={user.name} />
)}
{isLoggedIn ? (
<IconButton
aria-label="show more"
aria-haspopup="true"
onClick={handlePopupMenuOpen}
color="primary"
>
<MenuIcon />
</IconButton>
) : (
<Fab
variant="extended"
size="medium"
color="primary"
aria-label="login"
className={classes.loginButton}
onClick={() => setOpenAuthDialog(true)}
>
Login
</Fab>
)}
</div>
<div className={classes.sectionMobile}>
<IconButton
aria-label="show more"
aria-haspopup="true"
color="inherit"
onClick={toggleDrawer}
>
<MenuIcon />
</IconButton>
</div>
</Toolbar>
</AppBar>
{popupMenu}
{mobileDrawerMenu}
<AuthDialog open={openAuthDialog} onClose={() => setOpenAuthDialog(false)} />
</div>
);
}
Example #27
Source File: Header.tsx From frontend with Apache License 2.0 | 4 votes |
Header: FunctionComponent = () => {
const [avatarMenuRef, setAvatarMenuRef] = React.useState<null | HTMLElement>(null);
const [helpMenuRef, setHelpMenuRef] = React.useState<null | HTMLElement>(null);
const { loggedIn, user } = useUserState();
const authDispatch = useUserDispatch();
const styleMenuItem = {
display: "flex",
alignItems: "center",
};
const handleMenuClose = () => {
setAvatarMenuRef(null);
setHelpMenuRef(null);
};
const closeMenuAndOpenLink = () => {
handleMenuClose();
window.open("https://github.com/Visual-Regression-Tracker/Visual-Regression-Tracker/issues/new", "_blank");
};
const getVRTVersion = (): string => {
//For cypress tests, window._env_ variable may be undefined, so return a dummy value.
return window._env_ ? window._env_.VRT_VERSION : "5.0.0";
};
const renderHelpMenu = (
<Menu
anchorEl={helpMenuRef}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id="headerHelpMenu"
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={!!helpMenuRef}
onClose={handleMenuClose}
>
<MenuItem onClick={handleMenuClose} >
<GuidedTour />
</MenuItem>
<MenuItem onClick={closeMenuAndOpenLink} style={styleMenuItem}>
<IconButton size="small">
<GitHub />
</IconButton>
Open an issue in GitHub
</MenuItem>
<hr />
<MenuItem style={{
justifyContent: "center"
}}>
VRT Version : {getVRTVersion()}
</MenuItem>
</Menu >
);
const renderAvatarMenu = (
<Menu
anchorEl={avatarMenuRef}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id="headerAvatarMenu"
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={!!avatarMenuRef}
onClose={handleMenuClose}
>
{user?.role === "admin" && (
<MenuItem
component={Link}
to={routes.USER_LIST_PAGE}
onClick={handleMenuClose}
style={styleMenuItem}
>
<IconButton size="small">
<People />
</IconButton>
Users
</MenuItem>
)}
<MenuItem
component={Link}
to={routes.PROJECT_LIST_PAGE}
onClick={handleMenuClose}
style={styleMenuItem}
>
<IconButton size="small">
<AllInbox />
</IconButton>
Projects
</MenuItem>
<MenuItem
component={Link}
to={routes.PROFILE_PAGE}
onClick={handleMenuClose}
style={styleMenuItem}
>
<IconButton size="small">
<Face />
</IconButton>
Profile
</MenuItem>
<MenuItem
onClick={() => {
handleMenuClose();
logout(authDispatch);
}}
data-testid="logoutBtn"
>
<IconButton size="small">
<SettingsPower />
</IconButton>
Logout
</MenuItem>
</Menu>
);
return (
<React.Fragment>
<AppBar position="static" color="default">
<Toolbar>
<Grid container justifyContent="space-between" alignItems="center">
<Grid item>
<Link to="/">
<img src={logo} width="60" height="60" alt="logo" />
</Link>
</Grid>
<Grid item>
<Grid
container
justifyContent="space-between"
alignItems="center"
>
<IconButton onClick={(event: React.MouseEvent<HTMLElement>) =>
setHelpMenuRef(event.currentTarget)
}>
<Avatar>
<HelpOutline />
</Avatar>
</IconButton>
{loggedIn && (
<IconButton
onClick={(event: React.MouseEvent<HTMLElement>) =>
setAvatarMenuRef(event.currentTarget)
}
>
<Avatar>{`${user?.firstName[0]}${user?.lastName[0]}`}</Avatar>
</IconButton>
)}
</Grid>
</Grid>
</Grid>
</Toolbar>
</AppBar>
{renderAvatarMenu}
{renderHelpMenu}
</React.Fragment>
);
}
Example #28
Source File: index.tsx From frontend with Apache License 2.0 | 4 votes |
BuildList: FunctionComponent = () => {
const classes = useStyles();
const navigate = useNavigate();
const { buildList, selectedBuild, loading, total, take } = useBuildState();
const buildDispatch = useBuildDispatch();
const { enqueueSnackbar } = useSnackbar();
const { selectedProjectId } = useProjectState();
const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false);
const [editDialogOpen, setEditDialogOpen] = React.useState(false);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [menuBuild, setMenuBuild] = React.useState<Build | null>();
const [newCiBuildId, setNewCiBuildId] = React.useState("");
const [paginationPage, setPaginationPage] = React.useState(1);
const handleMenuClick = (
event: React.MouseEvent<HTMLElement>,
build: Build
) => {
event.stopPropagation();
setAnchorEl(event.currentTarget);
setMenuBuild(build);
};
const handleMenuClose = () => {
setMenuBuild(null);
};
const toggleDeleteDialogOpen = () => {
setDeleteDialogOpen(!deleteDialogOpen);
};
const toggleEditDialogOpen = () => {
setEditDialogOpen(!editDialogOpen);
};
const selectBuildCalback = React.useCallback(
(id?: string) => navigate(buildTestRunLocation(id)),
[navigate]
);
const handlePaginationChange = React.useCallback(
(page: number) => {
setPaginationPage(page);
if (selectedProjectId) {
buildDispatch({ type: "request" });
buildsService
.getList(selectedProjectId, take, take * (page - 1))
.then((payload) => {
buildDispatch({ type: "get", payload });
})
.catch((err: string) =>
enqueueSnackbar(err, {
variant: "error",
})
);
}
},
[buildDispatch, enqueueSnackbar, selectedProjectId, take]
);
React.useEffect(() => {
handlePaginationChange(1);
}, [handlePaginationChange]);
return (
<React.Fragment>
<Box height="91%" overflow="auto">
<List>
{loading ? (
<SkeletonList />
) : buildList.length === 0 ? (
<Typography variant="h5">No builds</Typography>
) : (
buildList.map((build) => (
<React.Fragment key={build.id}>
<ListItem
selected={selectedBuild?.id === build.id}
button
onClick={() => selectBuildCalback(build.id)}
classes={{
container: classes.listItem,
}}
>
<ListItemText
disableTypography
primary={
<Typography
variant="subtitle2"
style={{
wordWrap: "break-word",
}}
>
{`#${build.number} ${build.ciBuildId || ""}`}
</Typography>
}
secondary={
<Grid container direction="column">
<Grid item>
<Typography variant="caption" color="textPrimary">
{formatDateTime(build.createdAt)}
</Typography>
</Grid>
<Grid item>
<Grid container justifyContent="space-between">
<Grid item>
<Tooltip title={build.branchName}>
<Chip
size="small"
label={build.branchName}
style={{ maxWidth: 180 }}
/>
</Tooltip>
</Grid>
<Grid item>
<BuildStatusChip status={build.status} />
</Grid>
</Grid>
</Grid>
</Grid>
}
/>
<ListItemSecondaryAction
className={classes.listItemSecondaryAction}
>
<IconButton
onClick={(event) => handleMenuClick(event, build)}
>
<MoreVert />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
{build.isRunning && <LinearProgress />}
</React.Fragment>
))
)}
</List>
</Box>
<Box height="9%">
<Grid container justifyContent="center">
<Grid item>
<Pagination
size="small"
defaultPage={1}
page={paginationPage}
count={Math.ceil(total / take)}
onChange={(event, page) => handlePaginationChange(page)}
/>
</Grid>
</Grid>
</Box>
{menuBuild && (
<Menu anchorEl={anchorEl} open={!!menuBuild} onClose={handleMenuClose}>
{menuBuild.isRunning && (
<MenuItem
onClick={() => {
buildsService
.update(menuBuild.id, { isRunning: false })
.then((b) =>
enqueueSnackbar(`${menuBuild.id} finished`, {
variant: "success",
})
)
.catch((err) =>
enqueueSnackbar(err, {
variant: "error",
})
);
handleMenuClose();
}}
>
Stop
</MenuItem>
)}
<MenuItem onClick={toggleEditDialogOpen}>Edit CI Build</MenuItem>
<MenuItem onClick={toggleDeleteDialogOpen}>Delete</MenuItem>
</Menu>
)}
{menuBuild && (
<BaseModal
open={editDialogOpen}
title={"Edit CI Build ID"}
submitButtonText={"Edit"}
onCancel={toggleEditDialogOpen}
content={
<React.Fragment>
<Typography>{`Edit the ci build id for build: #${
menuBuild.number || menuBuild.id
}`}</Typography>
<TextValidator
name="newCiBuildId"
validators={["minStringLength:2"]}
errorMessages={["Enter at least two characters."]}
margin="dense"
id="name"
label="New CI Build Id"
type="text"
fullWidth
required
value={newCiBuildId}
inputProps={{
onChange: (event: any) =>
setNewCiBuildId((event.target as HTMLInputElement).value),
"data-testid": "newCiBuildId",
}}
/>
</React.Fragment>
}
onSubmit={() => {
buildsService
.update(menuBuild.id, {
ciBuildId: newCiBuildId,
})
.then((b) => {
toggleEditDialogOpen();
})
.catch((err) =>
enqueueSnackbar(err, {
variant: "error",
})
);
handleMenuClose();
}}
/>
)}
{menuBuild && (
<BaseModal
open={deleteDialogOpen}
title={"Delete Build"}
submitButtonText={"Delete"}
onCancel={toggleDeleteDialogOpen}
content={
<Typography>{`Are you sure you want to delete build: #${
menuBuild.number || menuBuild.id
}?`}</Typography>
}
onSubmit={() => {
deleteBuild(buildDispatch, menuBuild.id)
.then((build) => {
toggleDeleteDialogOpen();
enqueueSnackbar(
`Build #${menuBuild.number || menuBuild.id} deleted`,
{
variant: "success",
}
);
})
.then(() => handlePaginationChange(paginationPage))
.then(() => {
if (menuBuild.id === selectedBuild?.id) {
selectBuildCalback();
}
})
.catch((err) =>
enqueueSnackbar(err, {
variant: "error",
})
);
handleMenuClose();
}}
/>
)}
</React.Fragment>
);
}
Example #29
Source File: ModActions.tsx From ow-mod-manager with MIT License | 4 votes |
ModActions: React.FunctionComponent<Props> = ({ mod }) => {
const styles = useStyles();
const setLocalMods = useSetRecoilState(localModList);
const { owmlPath, alphaPath, owamlPath } = useRecoilValue(settingsState);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [isLoading, setIsLoading] = useRecoilState(
modIsLoadingState(mod.uniqueName)
);
const { startLoading, endLoading } = useLoading();
const setProgress = useSetRecoilState(modProgressState(mod.uniqueName));
const handleMoreClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const isModBroken = isBroken(mod);
const isModInstalled = mod !== undefined && isInstalled(mod);
const isModOutdated = isOutdated(mod);
const isModInstallable = mod.downloadUrl !== undefined;
const isModDownloadable =
!isLoading &&
isModInstallable &&
(!isModInstalled || isModBroken || isModOutdated);
const isInstallHighlighted =
!isLoading &&
!isModBroken &&
(isModOutdated || (mod.isRequired && !isModInstalled));
const handleActionError = (actionName: string, error: string) => {
debugConsole.error('error in action', actionName, error);
setLocalMods(getLocalMods(owmlPath, alphaPath, owamlPath));
};
const modActionHandler = (
handler: ModActionHandler<Promise<void> | void>,
actionName: string
) => async () => {
handleClose();
if (mod !== undefined) {
setIsLoading(true);
startLoading();
try {
await handler(mod, setProgress);
} catch (error) {
handleActionError(actionName, `${error}`);
} finally {
setProgress(0);
// TODO try something other than a SetTimeout here.
setTimeout(() => {
setIsLoading(false);
}, 1000);
endLoading();
}
}
};
const modActionHandlerSync = (
handler: ModActionHandlerSync<void>,
actionName: string
) => () => {
handleClose();
try {
handler(mod);
} catch (error) {
handleActionError(actionName, `${error}`);
}
};
const getEnableTooltip = () => {
if (mod.isRequired) {
return modsText.actions.disableRequired;
}
if (mod.isEnabled) {
return modsText.actions.disable;
}
if (isModInstalled) {
return modsText.actions.enable;
}
return '';
};
const getInstallTooltip = () => {
if (isLoading) {
return modsText.actions.loading;
}
if (isModBroken) {
return mod.remoteVersion
? modsText.actions.reinstall
: modsText.actions.cantReinstall;
}
if (isModOutdated && mod.remoteVersion) {
return modsText.actions.update(mod.remoteVersion);
}
if (isModInstalled) {
return modsText.actions.alreadyInstalled;
}
return modsText.actions.install;
};
return (
<Box display="flex" justifyContent="space-between">
<Tooltip title={getEnableTooltip()}>
<span>
<IconButton
size="small"
disabled={!isModInstalled || mod.isRequired}
onClick={modActionHandlerSync(toggleEnabled, 'enable toggle')}
>
{mod.isEnabled ? <CheckBoxIcon /> : <CheckboxBlankIcon />}
</IconButton>
</span>
</Tooltip>
<Tooltip title={getInstallTooltip()}>
<span>
<IconButton
onClick={modActionHandler(
isModBroken ? reinstall : install,
'install'
)}
disabled={!isModDownloadable}
size="small"
className={isInstallHighlighted ? styles.highlightedButton : ''}
>
{isLoading && <ModActionProgress modUniqueName={mod.uniqueName} />}
{!isLoading && (isModOutdated ? <UpdateIcon /> : <SaveIcon />)}
</IconButton>
</span>
</Tooltip>
<Tooltip title={modsText.actions.readme}>
<span>
<IconButton
disabled={!mod.repo}
size="small"
onClick={modActionHandlerSync(openReadme, 'repo open')}
>
<DescriptionIcon />
</IconButton>
</span>
</Tooltip>
<Tooltip title={modsText.actions.more}>
<span>
<IconButton size="small" onClick={handleMoreClick}>
<MoreIcon />
</IconButton>
</span>
</Tooltip>
<Menu
id="simple-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
TransitionComponent={undefined}
transitionDuration={0}
>
{mod.prerelease && (
<MenuItem
disabled={mod.localVersion === mod.prerelease.version}
onClick={modActionHandler(installPrerelease, 'install prerelease')}
>
<ListItemIcon>
<SaveIcon />
</ListItemIcon>
{modsText.actions.installPrerelease(mod.prerelease.version)}
</MenuItem>
)}
{isModInstalled && (
<MenuItem
disabled={!isModInstalled}
onClick={modActionHandlerSync(openModDirectory, 'directory open')}
>
<ListItemIcon>
<FolderIcon />
</ListItemIcon>
{modsText.actions.openDirectory}
</MenuItem>
)}
<MenuItem
disabled={!mod.repo}
onClick={modActionHandlerSync(openRepo, 'repo open')}
>
<ListItemIcon>
<GitHubIcon />
</ListItemIcon>
{modsText.actions.openRepo}
</MenuItem>
<MenuItem
disabled={mod.isRequired || !isModInstalled}
onClick={modActionHandlerSync(uninstall, 'uninstall')}
>
<ListItemIcon>
<DeleteIcon />
</ListItemIcon>
{modsText.actions.uninstall}
</MenuItem>
</Menu>
</Box>
);
}