index.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import {Fragment} from 'react';
  2. import {useTheme} from '@emotion/react';
  3. import styled from '@emotion/styled';
  4. import {openWidgetBuilderOverwriteModal} from 'sentry/actionCreators/modal';
  5. import type {OverwriteWidgetModalProps} from 'sentry/components/modals/widgetBuilder/overwriteWidgetModal';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import type {Organization} from 'sentry/types/organization';
  9. import {DisplayType} from 'sentry/views/dashboards/types';
  10. import type {WidgetTemplate} from 'sentry/views/dashboards/widgetLibrary/data';
  11. import {getTopNConvertedDefaultWidgets} from 'sentry/views/dashboards/widgetLibrary/data';
  12. import {normalizeQueries} from '../utils';
  13. import {Card} from './card';
  14. interface Props {
  15. bypassOverwriteModal: boolean;
  16. onWidgetSelect: (widget: WidgetTemplate) => void;
  17. organization: Organization;
  18. selectedWidgetId: string | null;
  19. }
  20. export function WidgetLibrary({
  21. bypassOverwriteModal,
  22. onWidgetSelect,
  23. selectedWidgetId,
  24. organization,
  25. }: Props) {
  26. const theme = useTheme();
  27. const defaultWidgets = getTopNConvertedDefaultWidgets(organization);
  28. function getLibrarySelectionHandler(
  29. widget: OverwriteWidgetModalProps['widget'],
  30. iconColor: OverwriteWidgetModalProps['iconColor']
  31. ) {
  32. return function handleWidgetSelect() {
  33. if (bypassOverwriteModal) {
  34. onWidgetSelect(widget);
  35. return;
  36. }
  37. openWidgetBuilderOverwriteModal({
  38. onConfirm: () => onWidgetSelect(widget),
  39. widget,
  40. iconColor,
  41. });
  42. };
  43. }
  44. return (
  45. <Fragment>
  46. <Header>{t('Widget Library')}</Header>
  47. <WidgetLibraryWrapper>
  48. {defaultWidgets.map((widget, index) => {
  49. const iconColor = theme.charts.getColorPalette(defaultWidgets.length - 2)[
  50. index
  51. ];
  52. const displayType =
  53. widget.displayType === DisplayType.TOP_N
  54. ? DisplayType.TABLE
  55. : widget.displayType;
  56. const normalizedQueries = normalizeQueries({
  57. displayType,
  58. queries: widget.queries,
  59. widgetType: widget.widgetType,
  60. });
  61. const newWidget = {
  62. ...widget,
  63. displayType,
  64. queries: normalizedQueries,
  65. };
  66. return (
  67. <CardHoverWrapper
  68. selected={selectedWidgetId === widget.id}
  69. key={widget.title}
  70. onClick={getLibrarySelectionHandler(newWidget, iconColor)}
  71. >
  72. <Card widget={newWidget} iconColor={iconColor} />
  73. </CardHoverWrapper>
  74. );
  75. })}
  76. </WidgetLibraryWrapper>
  77. </Fragment>
  78. );
  79. }
  80. const WidgetLibraryWrapper = styled('div')`
  81. display: flex;
  82. flex-direction: column;
  83. `;
  84. const Header = styled('h5')`
  85. /* to be aligned with the 30px of Layout.main padding */
  86. padding-left: calc(${space(2)} - ${space(0.25)});
  87. `;
  88. const CardHoverWrapper = styled('div')<{selected: boolean}>`
  89. /* to be aligned with the 30px of Layout.main padding - 1px of the widget item border */
  90. padding: calc(${space(2)} - 3px);
  91. border: 1px solid transparent;
  92. border-radius: ${p => p.theme.borderRadius};
  93. transition: border-color 0.3s ease;
  94. cursor: pointer;
  95. &:hover {
  96. border-color: ${p => p.theme.gray100};
  97. }
  98. ${p => p.selected && `border-color: ${p.theme.gray200};`}
  99. `;