|
1 |
| -import {Fragment, useEffect, useMemo, useState} from 'react'; |
| 1 | +import {Fragment, useCallback, useEffect, useState, type ReactNode} from 'react'; |
2 | 2 | import {css} from '@emotion/react';
|
3 | 3 | import styled from '@emotion/styled';
|
4 | 4 | import type {Location} from 'history';
|
@@ -27,6 +27,7 @@ import normalizeUrl from 'sentry/utils/url/normalizeUrl';
|
27 | 27 | import useApi from 'sentry/utils/useApi';
|
28 | 28 | import {useNavigate} from 'sentry/utils/useNavigate';
|
29 | 29 | import {useParams} from 'sentry/utils/useParams';
|
| 30 | +import {DashboardCreateLimitWrapper} from 'sentry/views/dashboards/createLimitWrapper'; |
30 | 31 | import {IndexedEventsSelectionAlert} from 'sentry/views/dashboards/indexedEventsSelectionAlert';
|
31 | 32 | import type {
|
32 | 33 | DashboardDetails,
|
@@ -68,7 +69,6 @@ export type AddToDashboardModalProps = {
|
68 | 69 | selection: PageFilters;
|
69 | 70 | widget: Widget;
|
70 | 71 | actions?: AddToDashboardModalActions[];
|
71 |
| - allowCreateNewDashboard?: boolean; |
72 | 72 | source?: DashboardWidgetSource;
|
73 | 73 | };
|
74 | 74 |
|
@@ -101,7 +101,6 @@ function AddToDashboardModal({
|
101 | 101 | selection,
|
102 | 102 | widget,
|
103 | 103 | actions = DEFAULT_ACTIONS,
|
104 |
| - allowCreateNewDashboard = true, |
105 | 104 | source,
|
106 | 105 | }: Props) {
|
107 | 106 | const api = useApi();
|
@@ -249,34 +248,44 @@ function AddToDashboardModal({
|
249 | 248 |
|
250 | 249 | const canSubmit = selectedDashboardId !== null;
|
251 | 250 |
|
252 |
| - const options = useMemo(() => { |
253 |
| - if (dashboards === null) { |
254 |
| - return null; |
255 |
| - } |
| 251 | + const getOptions = useCallback( |
| 252 | + ( |
| 253 | + hasReachedDashboardLimit: boolean, |
| 254 | + isLoading: boolean, |
| 255 | + limitMessage: ReactNode | null |
| 256 | + ) => { |
| 257 | + if (dashboards === null) { |
| 258 | + return null; |
| 259 | + } |
256 | 260 |
|
257 |
| - return [ |
258 |
| - allowCreateNewDashboard && { |
259 |
| - label: t('+ Create New Dashboard'), |
260 |
| - value: 'new', |
261 |
| - }, |
262 |
| - ...dashboards |
263 |
| - .filter(dashboard => |
264 |
| - // if adding from a dashboard, currentDashboardId will be set and we'll remove it from the list of options |
265 |
| - currentDashboardId ? dashboard.id !== currentDashboardId : true |
266 |
| - ) |
267 |
| - .map(({title, id, widgetDisplay}) => ({ |
268 |
| - label: title, |
269 |
| - value: id, |
270 |
| - disabled: widgetDisplay.length >= MAX_WIDGETS, |
271 |
| - tooltip: |
272 |
| - widgetDisplay.length >= MAX_WIDGETS && |
273 |
| - tct('Max widgets ([maxWidgets]) per dashboard reached.', { |
274 |
| - maxWidgets: MAX_WIDGETS, |
275 |
| - }), |
| 261 | + return [ |
| 262 | + { |
| 263 | + label: t('+ Create New Dashboard'), |
| 264 | + value: 'new', |
| 265 | + disabled: hasReachedDashboardLimit || isLoading, |
| 266 | + tooltip: hasReachedDashboardLimit ? limitMessage : undefined, |
276 | 267 | tooltipOptions: {position: 'right'},
|
277 |
| - })), |
278 |
| - ].filter(Boolean) as Array<SelectValue<string>>; |
279 |
| - }, [allowCreateNewDashboard, currentDashboardId, dashboards]); |
| 268 | + }, |
| 269 | + ...dashboards |
| 270 | + .filter(dashboard => |
| 271 | + // if adding from a dashboard, currentDashboardId will be set and we'll remove it from the list of options |
| 272 | + currentDashboardId ? dashboard.id !== currentDashboardId : true |
| 273 | + ) |
| 274 | + .map(({title, id, widgetDisplay}) => ({ |
| 275 | + label: title, |
| 276 | + value: id, |
| 277 | + disabled: widgetDisplay.length >= MAX_WIDGETS, |
| 278 | + tooltip: |
| 279 | + widgetDisplay.length >= MAX_WIDGETS && |
| 280 | + tct('Max widgets ([maxWidgets]) per dashboard reached.', { |
| 281 | + maxWidgets: MAX_WIDGETS, |
| 282 | + }), |
| 283 | + tooltipOptions: {position: 'right'}, |
| 284 | + })), |
| 285 | + ].filter(Boolean) as Array<SelectValue<string>>; |
| 286 | + }, |
| 287 | + [currentDashboardId, dashboards] |
| 288 | + ); |
280 | 289 |
|
281 | 290 | const widgetLegendState = new WidgetLegendSelectionState({
|
282 | 291 | location,
|
@@ -308,20 +317,24 @@ function AddToDashboardModal({
|
308 | 317 | </Header>
|
309 | 318 | <Body>
|
310 | 319 | <Wrapper>
|
311 |
| - <Select |
312 |
| - disabled={dashboards === null} |
313 |
| - menuPlacement="auto" |
314 |
| - name="dashboard" |
315 |
| - placeholder={t('Select Dashboard')} |
316 |
| - value={selectedDashboardId} |
317 |
| - options={options} |
318 |
| - onChange={(option: SelectValue<string>) => { |
319 |
| - if (option.disabled) { |
320 |
| - return; |
321 |
| - } |
322 |
| - setSelectedDashboardId(option.value); |
323 |
| - }} |
324 |
| - /> |
| 320 | + <DashboardCreateLimitWrapper> |
| 321 | + {({hasReachedDashboardLimit, isLoading, limitMessage}) => ( |
| 322 | + <Select |
| 323 | + disabled={dashboards === null} |
| 324 | + menuPlacement="auto" |
| 325 | + name="dashboard" |
| 326 | + placeholder={t('Select Dashboard')} |
| 327 | + value={selectedDashboardId} |
| 328 | + options={getOptions(hasReachedDashboardLimit, isLoading, limitMessage)} |
| 329 | + onChange={(option: SelectValue<string>) => { |
| 330 | + if (option.disabled) { |
| 331 | + return; |
| 332 | + } |
| 333 | + setSelectedDashboardId(option.value); |
| 334 | + }} |
| 335 | + /> |
| 336 | + )} |
| 337 | + </DashboardCreateLimitWrapper> |
325 | 338 | </Wrapper>
|
326 | 339 | <Wrapper>
|
327 | 340 | <SectionHeader title={t('Widget Name')} optional />
|
|
0 commit comments