index.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import * as React from 'react';
  2. import {useState} from 'react';
  3. import {css} from '@emotion/react';
  4. import styled from '@emotion/styled';
  5. import {ModalRenderProps} from 'app/actionCreators/modal';
  6. import Tag from 'app/components/tagDeprecated';
  7. import {t} from 'app/locale';
  8. import space from 'app/styles/space';
  9. import {Organization} from 'app/types';
  10. import {DashboardDetails, Widget} from 'app/views/dashboardsV2/types';
  11. import {WidgetTemplate} from 'app/views/dashboardsV2/widgetLibrary/data';
  12. import Button from '../../button';
  13. import ButtonBar from '../../buttonBar';
  14. import DashboardWidgetCustomTab from './customTab';
  15. import DashboardWidgetLibraryTab from './libraryTab';
  16. export type DashboardWidgetLibraryModalOptions = {
  17. organization: Organization;
  18. dashboard: DashboardDetails;
  19. onAddWidget: (widgets: Widget[]) => void;
  20. };
  21. export enum TAB {
  22. Library = 'library',
  23. Custom = 'custom',
  24. }
  25. type Props = ModalRenderProps & DashboardWidgetLibraryModalOptions;
  26. function DashboardWidgetLibraryModal({
  27. Header,
  28. Body,
  29. Footer,
  30. dashboard,
  31. closeModal,
  32. onAddWidget,
  33. }: Props) {
  34. const [tab, setTab] = useState(TAB.Library);
  35. const [selectedWidgets, setSelectedWidgets] = useState<WidgetTemplate[]>([]);
  36. const [errored, setErrored] = useState(false);
  37. function handleSubmit() {
  38. onAddWidget([...dashboard.widgets, ...selectedWidgets]);
  39. closeModal();
  40. }
  41. return (
  42. <React.Fragment>
  43. <Header closeButton>
  44. <h4>{t('Add Widget')}</h4>
  45. </Header>
  46. <Body>
  47. <StyledButtonBar active={tab}>
  48. <Button barId={TAB.Library} onClick={() => setTab(TAB.Library)}>
  49. {t('Library')}
  50. </Button>
  51. <Button barId={TAB.Custom} onClick={() => setTab(TAB.Custom)}>
  52. {t('Custom')}
  53. </Button>
  54. </StyledButtonBar>
  55. {tab === TAB.Library ? (
  56. <DashboardWidgetLibraryTab
  57. selectedWidgets={selectedWidgets}
  58. errored={errored}
  59. setSelectedWidgets={setSelectedWidgets}
  60. setErrored={setErrored}
  61. />
  62. ) : (
  63. <DashboardWidgetCustomTab />
  64. )}
  65. </Body>
  66. <Footer>
  67. <FooterButtonbar gap={1}>
  68. <Button
  69. external
  70. href="https://docs.sentry.io/product/dashboards/custom-dashboards/#widget-builder"
  71. >
  72. {t('Read the docs')}
  73. </Button>
  74. <div>
  75. <SelectedBadge data-test-id="selected-badge">
  76. {`${selectedWidgets.length} Selected`}
  77. </SelectedBadge>
  78. <Button
  79. data-test-id="confirm-widgets"
  80. priority="primary"
  81. type="button"
  82. onClick={(event: React.FormEvent) => {
  83. event.preventDefault();
  84. if (!!!selectedWidgets.length) {
  85. setErrored(true);
  86. return;
  87. }
  88. handleSubmit();
  89. }}
  90. >
  91. {t('Confirm')}
  92. </Button>
  93. </div>
  94. </FooterButtonbar>
  95. </Footer>
  96. </React.Fragment>
  97. );
  98. }
  99. export const modalCss = css`
  100. width: 100%;
  101. max-width: 700px;
  102. margin: 70px auto;
  103. `;
  104. const StyledButtonBar = styled(ButtonBar)`
  105. grid-template-columns: repeat(2, minmax(0, 1fr));
  106. margin-bottom: ${space(1)};
  107. `;
  108. const FooterButtonbar = styled(ButtonBar)`
  109. justify-content: space-between;
  110. width: 100%;
  111. `;
  112. const SelectedBadge = styled(Tag)`
  113. padding: 3px ${space(0.75)};
  114. display: inline-flex;
  115. align-items: center;
  116. margin-left: ${space(1)};
  117. margin-right: ${space(1)};
  118. top: -1px;
  119. `;
  120. export default DashboardWidgetLibraryModal;