From a4f7ed94c6dc730e2139dbb7ddfa42fccb2c4abe Mon Sep 17 00:00:00 2001 From: Arthur Knaus Date: Thu, 30 Jan 2025 10:00:38 +0100 Subject: [PATCH 1/2] ref(dynamic-sampling): Reduce code duplication --- .../organizationSampleRateField.tsx | 69 --------- .../organizationSampleRateInput.tsx | 135 ++++++++++++++++++ .../dynamicSampling/projectsEditTable.tsx | 97 +++---------- .../dynamicSampling/projectsPreviewTable.tsx | 27 +++- .../dynamicSampling/samplingBreakdown.tsx | 2 +- 5 files changed, 175 insertions(+), 155 deletions(-) delete mode 100644 static/app/views/settings/dynamicSampling/organizationSampleRateField.tsx create mode 100644 static/app/views/settings/dynamicSampling/organizationSampleRateInput.tsx diff --git a/static/app/views/settings/dynamicSampling/organizationSampleRateField.tsx b/static/app/views/settings/dynamicSampling/organizationSampleRateField.tsx deleted file mode 100644 index 7c54c10dbbb71c..00000000000000 --- a/static/app/views/settings/dynamicSampling/organizationSampleRateField.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import {css} from '@emotion/react'; -import styled from '@emotion/styled'; - -import FieldGroup from 'sentry/components/forms/fieldGroup'; -import {Tooltip} from 'sentry/components/tooltip'; -import {t} from 'sentry/locale'; -import {PercentInput} from 'sentry/views/settings/dynamicSampling/percentInput'; -import {useHasDynamicSamplingWriteAccess} from 'sentry/views/settings/dynamicSampling/utils/access'; -import {organizationSamplingForm} from 'sentry/views/settings/dynamicSampling/utils/organizationSamplingForm'; - -const {useFormField} = organizationSamplingForm; - -export function OrganizationSampleRateField() { - const field = useFormField('targetSampleRate'); - const hasAccess = useHasDynamicSamplingWriteAccess(); - - return ( - {t('Target Sample Rate')}} - help={t( - 'Set a global sample rate for your entire organization. This will determine how much incoming traffic should be stored across all your projects.' - )} - > - - - field.onChange(event.target.value)} - /> - - {field.error ? ( - {field.error} - ) : field.hasChanged ? ( - {t('previous: %f%%', field.initialValue)} - ) : null} - - - ); -} - -const PreviousValue = styled('span')` - font-size: ${p => p.theme.fontSizeExtraSmall}; - color: ${p => p.theme.subText}; -`; - -const ErrorMessage = styled('span')` - font-size: ${p => p.theme.fontSizeExtraSmall}; - color: ${p => p.theme.error}; -`; - -const InputWrapper = styled('div')` - padding-top: 8px; - height: 58px; - display: flex; - flex-direction: column; - gap: 4px; -`; diff --git a/static/app/views/settings/dynamicSampling/organizationSampleRateInput.tsx b/static/app/views/settings/dynamicSampling/organizationSampleRateInput.tsx new file mode 100644 index 00000000000000..e6a3bcc590710d --- /dev/null +++ b/static/app/views/settings/dynamicSampling/organizationSampleRateInput.tsx @@ -0,0 +1,135 @@ +import type React from 'react'; +import {useRef} from 'react'; +import styled from '@emotion/styled'; + +import {Button} from 'sentry/components/button'; +import {Tooltip} from 'sentry/components/tooltip'; +import {IconEdit} from 'sentry/icons'; +import {t} from 'sentry/locale'; +import {space} from 'sentry/styles/space'; +import {PercentInput} from 'sentry/views/settings/dynamicSampling/percentInput'; + +interface Props { + hasAccess: boolean; + help: React.ReactNode; + label: React.ReactNode; + onChange: (value: string) => void; + previousValue: string; + showPreviousValue: boolean; + value: string; + error?: string; + isBulkEditActive?: boolean; + isBulkEditEnabled?: boolean; + onBulkEditChange?: (value: boolean) => void; +} + +export function OrganizationSampleRateInput({ + value, + onChange, + hasAccess, + isBulkEditEnabled, + isBulkEditActive, + label, + help, + error, + previousValue, + showPreviousValue, + onBulkEditChange, +}: Props) { + const inputRef = useRef(null); + const showBulkEditButton = hasAccess && isBulkEditEnabled && !isBulkEditActive; + return ( + + + + {help} + + + + {showBulkEditButton && ( +