1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import React from 'react';
6 import { connect, DispatchProp } from 'react-redux';
7 import Typography from '@material-ui/core/Typography';
8 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
9 import { Tooltip } from '@material-ui/core';
10 import { CopyIcon } from 'components/icon/icon';
11 import CopyToClipboard from 'react-copy-to-clipboard';
12 import { ArvadosTheme } from 'common/custom-theme';
13 import classnames from "classnames";
14 import { Link } from 'react-router-dom';
15 import { RootState } from "store/store";
16 import { FederationConfig, getNavUrl } from "routes/routes";
17 import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
19 type CssRules = 'attribute' | 'label' | 'value' | 'lowercaseValue' | 'link' | 'copyIcon';
21 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
23 marginBottom: ".6 rem"
26 boxSizing: 'border-box',
27 color: theme.palette.grey["600"],
32 boxSizing: 'border-box',
33 alignItems: 'flex-start'
36 textTransform: 'lowercase'
39 color: theme.palette.primary.main,
40 textDecoration: 'none',
41 overflowWrap: 'break-word',
45 marginLeft: theme.spacing.unit,
46 color: theme.palette.grey["600"],
55 interface DetailsAttributeDataProps {
58 value?: React.ReactNode;
60 lowercaseValue?: boolean;
62 children?: React.ReactNode;
63 onValueClick?: () => void;
66 uuidEnhancer?: Function;
69 type DetailsAttributeProps = DetailsAttributeDataProps & WithStyles<CssRules> & FederationConfig & DispatchProp;
71 const mapStateToProps = ({ auth }: RootState): FederationConfig => ({
72 localCluster: auth.localCluster,
73 remoteHostsConfig: auth.remoteHostsConfig,
74 sessions: auth.sessions
77 export const DetailsAttribute = connect(mapStateToProps)(withStyles(styles)(
78 class extends React.Component<DetailsAttributeProps> {
80 onCopy = (message: string) => {
81 this.props.dispatch(snackbarActions.OPEN_SNACKBAR({
84 kind: SnackbarKind.SUCCESS
89 const { uuidEnhancer, link, value, classes, linkToUuid,
90 localCluster, remoteHostsConfig, sessions } = this.props;
91 let valueNode: React.ReactNode;
94 const uuid = uuidEnhancer ? uuidEnhancer(linkToUuid) : linkToUuid;
95 const linkUrl = getNavUrl(linkToUuid || "", { localCluster, remoteHostsConfig, sessions });
96 if (linkUrl[0] === '/') {
97 valueNode = <Link to={linkUrl} className={classes.link}>{uuid}</Link>;
99 valueNode = <a href={linkUrl} className={classes.link} target='_blank' rel="noopener noreferrer">{uuid}</a>;
102 valueNode = <a href={link} className={classes.link} target='_blank' rel="noopener noreferrer">{value}</a>;
107 return <DetailsAttributeComponent {...this.props} value={valueNode} onCopy={this.onCopy} />;
112 interface DetailsAttributeComponentProps {
113 value: React.ReactNode;
114 onCopy?: (msg: string) => void;
117 export const DetailsAttributeComponent = withStyles(styles)(
118 (props: DetailsAttributeDataProps & WithStyles<CssRules> & DetailsAttributeComponentProps) =>
119 <Typography component="div" className={props.classes.attribute} data-cy={`details-panel-${props.label.toLowerCase()}`}>
120 <Typography component="div" className={classnames([props.classes.label, props.classLabel])}>{props.label}</Typography>
122 onClick={props.onValueClick}
124 className={classnames([props.classes.value, props.classValue, { [props.classes.lowercaseValue]: props.lowercaseValue }])}>
127 {(props.linkToUuid || props.copyValue) && props.onCopy && <Tooltip title="Copy link to clipboard">
128 <span className={props.classes.copyIcon}>
129 <CopyToClipboard text={props.linkToUuid || props.copyValue || ""} onCopy={() => props.onCopy!("Copied")}>