index.tsx 3.4 KB

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