index.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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, WidgetType} 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. let defaultWidgets = getTopNConvertedDefaultWidgets();
  28. if (!organization.features.includes('dashboards-rh-widget')) {
  29. defaultWidgets = defaultWidgets.filter(
  30. widget => !(widget.widgetType === WidgetType.RELEASE)
  31. );
  32. }
  33. function getLibrarySelectionHandler(
  34. widget: OverwriteWidgetModalProps['widget'],
  35. iconColor: OverwriteWidgetModalProps['iconColor']
  36. ) {
  37. return function handleWidgetSelect() {
  38. if (bypassOverwriteModal) {
  39. onWidgetSelect(widget);
  40. return;
  41. }
  42. openWidgetBuilderOverwriteModal({
  43. onConfirm: () => onWidgetSelect(widget),
  44. widget,
  45. iconColor,
  46. });
  47. };
  48. }
  49. return (
  50. <Fragment>
  51. <Header>{t('Widget Library')}</Header>
  52. <WidgetLibraryWrapper>
  53. {defaultWidgets.map((widget, index) => {
  54. const iconColor = theme.charts.getColorPalette(defaultWidgets.length - 2)[
  55. index
  56. ];
  57. const displayType =
  58. widget.displayType === DisplayType.TOP_N
  59. ? DisplayType.TABLE
  60. : widget.displayType;
  61. const normalizedQueries = normalizeQueries({
  62. displayType,
  63. queries: widget.queries,
  64. widgetType: widget.widgetType,
  65. });
  66. const newWidget = {
  67. ...widget,
  68. displayType,
  69. queries: normalizedQueries,
  70. };
  71. return (
  72. <CardHoverWrapper
  73. selected={selectedWidgetId === widget.id}
  74. key={widget.title}
  75. onClick={getLibrarySelectionHandler(newWidget, iconColor)}
  76. >
  77. <Card widget={newWidget} iconColor={iconColor} />
  78. </CardHoverWrapper>
  79. );
  80. })}
  81. </WidgetLibraryWrapper>
  82. </Fragment>
  83. );
  84. }
  85. const WidgetLibraryWrapper = styled('div')`
  86. display: flex;
  87. flex-direction: column;
  88. `;
  89. const Header = styled('h5')`
  90. /* to be aligned with the 30px of Layout.main padding */
  91. padding-left: calc(${space(2)} - ${space(0.25)});
  92. `;
  93. const CardHoverWrapper = styled('div')<{selected: boolean}>`
  94. /* to be aligned with the 30px of Layout.main padding - 1px of the widget item border */
  95. padding: calc(${space(2)} - 3px);
  96. border: 1px solid transparent;
  97. border-radius: ${p => p.theme.borderRadius};
  98. transition: border-color 0.3s ease;
  99. cursor: pointer;
  100. &:hover {
  101. border-color: ${p => p.theme.gray100};
  102. }
  103. ${p => p.selected && `border-color: ${p.theme.gray200};`}
  104. `;