mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
171 lines
4.8 KiB
TypeScript
171 lines
4.8 KiB
TypeScript
// Copyright (c) 2017 Uber Technologies, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import { css } from '@emotion/css';
|
|
import cx from 'classnames';
|
|
import * as React from 'react';
|
|
import IoIosArrowDown from 'react-icons/lib/io/ios-arrow-down';
|
|
import IoIosArrowRight from 'react-icons/lib/io/ios-arrow-right';
|
|
|
|
import { GrafanaTheme2 } from '@grafana/data';
|
|
import { useStyles2 } from '@grafana/ui';
|
|
|
|
import { autoColor } from '../../Theme';
|
|
import { TNil } from '../../types';
|
|
import { TraceKeyValuePair, TraceLink } from '../../types/trace';
|
|
import { uAlignIcon, uTxEllipsis } from '../../uberUtilityStyles';
|
|
|
|
import * as markers from './AccordianKeyValues.markers';
|
|
import KeyValuesTable from './KeyValuesTable';
|
|
|
|
export const getStyles = (theme: GrafanaTheme2) => {
|
|
return {
|
|
header: css`
|
|
label: header;
|
|
cursor: pointer;
|
|
overflow: hidden;
|
|
padding: 0.25em 0.1em;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
&:hover {
|
|
background: ${autoColor(theme, '#e8e8e8')};
|
|
}
|
|
`,
|
|
headerEmpty: css`
|
|
label: headerEmpty;
|
|
background: none;
|
|
cursor: initial;
|
|
`,
|
|
headerHighContrast: css`
|
|
label: headerHighContrast;
|
|
&:hover {
|
|
background: ${autoColor(theme, '#ddd')};
|
|
}
|
|
`,
|
|
emptyIcon: css`
|
|
label: emptyIcon;
|
|
color: ${autoColor(theme, '#aaa')};
|
|
`,
|
|
summary: css`
|
|
label: summary;
|
|
display: inline;
|
|
list-style: none;
|
|
padding: 0;
|
|
`,
|
|
summaryItem: css`
|
|
label: summaryItem;
|
|
display: inline;
|
|
margin-left: 0.7em;
|
|
padding-right: 0.5rem;
|
|
border-right: 1px solid ${autoColor(theme, '#ddd')};
|
|
&:last-child {
|
|
padding-right: 0;
|
|
border-right: none;
|
|
}
|
|
`,
|
|
summaryLabel: css`
|
|
label: summaryLabel;
|
|
color: ${autoColor(theme, '#777')};
|
|
`,
|
|
summaryDelim: css`
|
|
label: summaryDelim;
|
|
color: ${autoColor(theme, '#bbb')};
|
|
padding: 0 0.2em;
|
|
`,
|
|
};
|
|
};
|
|
|
|
export type AccordianKeyValuesProps = {
|
|
className?: string | TNil;
|
|
data: TraceKeyValuePair[];
|
|
highContrast?: boolean;
|
|
interactive?: boolean;
|
|
isOpen: boolean;
|
|
label: string;
|
|
linksGetter: ((pairs: TraceKeyValuePair[], index: number) => TraceLink[]) | TNil;
|
|
onToggle?: null | (() => void);
|
|
};
|
|
|
|
// export for tests
|
|
export function KeyValuesSummary(props: { data?: TraceKeyValuePair[] }) {
|
|
const { data } = props;
|
|
const styles = useStyles2(getStyles);
|
|
|
|
if (!Array.isArray(data) || !data.length) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<ul className={styles.summary}>
|
|
{data.map((item, i) => (
|
|
// `i` is necessary in the key because item.key can repeat
|
|
<li className={styles.summaryItem} key={`${item.key}-${i}`}>
|
|
<span className={styles.summaryLabel}>{item.key}</span>
|
|
<span className={styles.summaryDelim}>=</span>
|
|
{String(item.value)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
);
|
|
}
|
|
|
|
KeyValuesSummary.defaultProps = {
|
|
data: null,
|
|
};
|
|
|
|
export default function AccordianKeyValues(props: AccordianKeyValuesProps) {
|
|
const { className, data, highContrast, interactive, isOpen, label, linksGetter, onToggle } = props;
|
|
const isEmpty = !Array.isArray(data) || !data.length;
|
|
const styles = useStyles2(getStyles);
|
|
const iconCls = cx(uAlignIcon, { [styles.emptyIcon]: isEmpty });
|
|
let arrow: React.ReactNode | null = null;
|
|
let headerProps: {} | null = null;
|
|
if (interactive) {
|
|
arrow = isOpen ? <IoIosArrowDown className={iconCls} /> : <IoIosArrowRight className={iconCls} />;
|
|
headerProps = {
|
|
'aria-checked': isOpen,
|
|
onClick: isEmpty ? null : onToggle,
|
|
role: 'switch',
|
|
};
|
|
}
|
|
|
|
return (
|
|
<div className={cx(className, uTxEllipsis)}>
|
|
<div
|
|
className={cx(styles.header, {
|
|
[styles.headerEmpty]: isEmpty,
|
|
[styles.headerHighContrast]: highContrast && !isEmpty,
|
|
})}
|
|
{...headerProps}
|
|
data-testid="AccordianKeyValues--header"
|
|
>
|
|
{arrow}
|
|
<strong data-test={markers.LABEL}>
|
|
{label}
|
|
{isOpen || ':'}
|
|
</strong>
|
|
{!isOpen && <KeyValuesSummary data={data} />}
|
|
</div>
|
|
{isOpen && <KeyValuesTable data={data} linksGetter={linksGetter} />}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
AccordianKeyValues.defaultProps = {
|
|
className: null,
|
|
highContrast: false,
|
|
interactive: true,
|
|
onToggle: null,
|
|
};
|