components.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import type {ReactNode} from 'react';
  2. import {Fragment, useState} from 'react';
  3. import styled from '@emotion/styled';
  4. import {KeyValueTable, KeyValueTableRow} from 'sentry/components/keyValueTable';
  5. import {Tooltip} from 'sentry/components/tooltip';
  6. import {IconChevron} from 'sentry/icons';
  7. import {t} from 'sentry/locale';
  8. import {space} from 'sentry/styles/space';
  9. export const Indent = styled('div')`
  10. padding-left: ${space(4)};
  11. `;
  12. const NotFoundText = styled('span')`
  13. color: ${p => p.theme.subText};
  14. font-size: ${p => p.theme.fontSizeSmall};
  15. `;
  16. const WarningText = styled('span')`
  17. color: ${p => p.theme.errorText};
  18. `;
  19. export function Warning({warnings}: {warnings: string[]}) {
  20. if (warnings.includes('JSON_TRUNCATED') || warnings.includes('TEXT_TRUNCATED')) {
  21. return (
  22. <WarningText>{t('Truncated (~~) due to exceeding 150k characters')}</WarningText>
  23. );
  24. }
  25. if (warnings.includes('INVALID_JSON')) {
  26. return <WarningText>{t('Invalid JSON')}</WarningText>;
  27. }
  28. return null;
  29. }
  30. export function SizeTooltip({children}: {children: ReactNode}) {
  31. return (
  32. <Tooltip
  33. title={t('It is possible the network transfer size is smaller due to compression.')}
  34. >
  35. {children}
  36. </Tooltip>
  37. );
  38. }
  39. export type KeyValueTuple = {
  40. key: string;
  41. value: string | ReactNode;
  42. type?: 'warning' | 'error';
  43. };
  44. export function keyValueTableOrNotFound(data: KeyValueTuple[], notFoundText: string) {
  45. return data.length ? (
  46. <StyledKeyValueTable noMargin>
  47. {data.map(({key, value, type}) => (
  48. <KeyValueTableRow
  49. key={key}
  50. keyName={key}
  51. type={type}
  52. value={<ValueContainer>{value}</ValueContainer>}
  53. />
  54. ))}
  55. </StyledKeyValueTable>
  56. ) : (
  57. <Indent>
  58. <NotFoundText>{notFoundText}</NotFoundText>
  59. </Indent>
  60. );
  61. }
  62. const ValueContainer = styled('span')`
  63. overflow: auto;
  64. `;
  65. const SectionTitle = styled('dt')``;
  66. const SectionTitleExtra = styled('span')`
  67. flex-grow: 1;
  68. text-align: right;
  69. font-weight: ${p => p.theme.fontWeightNormal};
  70. `;
  71. const SectionData = styled('dd')`
  72. font-size: ${p => p.theme.fontSizeExtraSmall};
  73. `;
  74. const ToggleButton = styled('button')`
  75. background: ${p => p.theme.background};
  76. border: 0;
  77. color: ${p => p.theme.headingColor};
  78. font-size: ${p => p.theme.fontSizeSmall};
  79. font-weight: ${p => p.theme.fontWeightBold};
  80. line-height: ${p => p.theme.text.lineHeightBody};
  81. width: 100%;
  82. display: flex;
  83. align-items: center;
  84. justify-content: flex-start;
  85. gap: ${space(1)};
  86. padding: ${space(0.5)} ${space(1)};
  87. :hover {
  88. background: ${p => p.theme.backgroundSecondary};
  89. }
  90. `;
  91. export function SectionItem({
  92. children,
  93. title,
  94. titleExtra,
  95. }: {
  96. children: ReactNode;
  97. title: ReactNode;
  98. titleExtra?: ReactNode;
  99. }) {
  100. const [isOpen, setIsOpen] = useState(true);
  101. return (
  102. <Fragment>
  103. <SectionTitle>
  104. <ToggleButton aria-label={t('toggle section')} onClick={() => setIsOpen(!isOpen)}>
  105. <IconChevron direction={isOpen ? 'down' : 'right'} size="xs" />
  106. {title}
  107. {titleExtra ? <SectionTitleExtra>{titleExtra}</SectionTitleExtra> : null}
  108. </ToggleButton>
  109. </SectionTitle>
  110. <SectionData>{isOpen ? children : null}</SectionData>
  111. </Fragment>
  112. );
  113. }
  114. const StyledKeyValueTable = styled(KeyValueTable)`
  115. & > dt {
  116. font-size: ${p => p.theme.fontSizeSmall};
  117. padding-left: ${space(4)};
  118. }
  119. & > dd {
  120. ${p => p.theme.overflowEllipsis};
  121. font-size: ${p => p.theme.fontSizeSmall};
  122. display: flex;
  123. justify-content: flex-end;
  124. white-space: normal;
  125. text-align: right;
  126. }
  127. `;