components.tsx 3.6 KB

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