Browse Source

ref(dashboards): Simplify `BigNumberWidget` data props (#79853)

Addresses a comment from the spec, and simplifies the code, and makes it
more explicit. Instead of passing, awkwardly, a `data` prop which is an
array of objects, pass a single value. The benefits of passing the array
of objects did not pan out. It offered the convenience of passing in
Discover data, but it's not actually that helpful. Instead, this API is
more explicit and allows for cleaner handling of equations, and other
complicated cases.
George Gritsouk 4 months ago
parent
commit
dce52e2f09

+ 25 - 61
static/app/views/dashboards/widgets/bigNumberWidget/bigNumberWidget.spec.tsx

@@ -10,11 +10,8 @@ describe('BigNumberWidget', () => {
         <BigNumberWidget
           title="EPS"
           description="Number of events per second"
-          data={[
-            {
-              'eps()': 0.01087819860850493,
-            },
-          ]}
+          value={0.01087819860850493}
+          field="eps()"
           meta={{
             fields: {
               'eps()': 'rate',
@@ -34,7 +31,8 @@ describe('BigNumberWidget', () => {
     it('Explains missing data', () => {
       render(
         <BigNumberWidget
-          data={[{}]}
+          value={undefined}
+          field={'p95(span.duration)'}
           meta={{
             fields: {
               'p95(span.duration)': 'number',
@@ -49,11 +47,8 @@ describe('BigNumberWidget', () => {
     it('Explains non-numeric data', () => {
       render(
         <BigNumberWidget
-          data={[
-            {
-              'count()': Infinity,
-            },
-          ]}
+          value={Infinity}
+          field="count()"
           meta={{
             fields: {
               'count()': 'number',
@@ -68,11 +63,8 @@ describe('BigNumberWidget', () => {
     it('Formats dates', () => {
       render(
         <BigNumberWidget
-          data={[
-            {
-              'max(timestamp)': '2024-10-17T16:08:07+00:00',
-            },
-          ]}
+          value={'2024-10-17T16:08:07+00:00'}
+          field="max(timestamp)"
           meta={{
             fields: {
               'max(timestamp)': 'date',
@@ -90,11 +82,8 @@ describe('BigNumberWidget', () => {
     it('Renders strings', () => {
       render(
         <BigNumberWidget
-          data={[
-            {
-              'any(transaction)': '/api/0/fetch',
-            },
-          ]}
+          value={'/api/0/fetch'}
+          field="any(transaction)"
           meta={{
             fields: {
               'max(timestamp)': 'string',
@@ -109,11 +98,8 @@ describe('BigNumberWidget', () => {
     it('Formats duration data', () => {
       render(
         <BigNumberWidget
-          data={[
-            {
-              'p95(span.duration)': 17.28,
-            },
-          ]}
+          value={17.28}
+          field="p95(span.duration)"
           meta={{
             fields: {
               'p95(span.duration)': 'duration',
@@ -131,11 +117,8 @@ describe('BigNumberWidget', () => {
     it('Shows the full unformatted value on hover', async () => {
       render(
         <BigNumberWidget
-          data={[
-            {
-              'count()': 178451214,
-            },
-          ]}
+          value={178451214}
+          field="count()"
           meta={{
             fields: {
               'count()': 'integer',
@@ -156,11 +139,8 @@ describe('BigNumberWidget', () => {
       render(
         <BigNumberWidget
           title="Count"
-          data={[
-            {
-              'count()': 178451214,
-            },
-          ]}
+          value={178451214}
+          field="count()"
           maximumValue={100000000}
           meta={{
             fields: {
@@ -232,16 +212,9 @@ describe('BigNumberWidget', () => {
       render(
         <BigNumberWidget
           title="http_response_code_rate(500)"
-          data={[
-            {
-              'http_response_code_rate(500)': 0.14227123,
-            },
-          ]}
-          previousPeriodData={[
-            {
-              'http_response_code_rate(500)': 0.1728139,
-            },
-          ]}
+          value={0.14227123}
+          previousPeriodValue={0.1728139}
+          field="http_response_code_rate(500)"
           meta={{
             fields: {
               'http_response_code_rate(500)': 'percentage',
@@ -262,11 +235,8 @@ describe('BigNumberWidget', () => {
     it('Evaluates the current value against a threshold', async () => {
       render(
         <BigNumberWidget
-          data={[
-            {
-              'eps()': 14.227123,
-            },
-          ]}
+          value={14.227123}
+          field="eps()"
           meta={{
             fields: {
               'eps()': 'rate',
@@ -294,11 +264,8 @@ describe('BigNumberWidget', () => {
     it('Normalizes the units', () => {
       render(
         <BigNumberWidget
-          data={[
-            {
-              'mystery_error_rate()': 135, //  2.25/s
-            },
-          ]}
+          value={135} //  2.25/s
+          field="mystery_error_rate()"
           meta={{
             fields: {
               'mystery_error_rate()': 'rate',
@@ -323,11 +290,8 @@ describe('BigNumberWidget', () => {
     it('Respects the preferred polarity', () => {
       render(
         <BigNumberWidget
-          data={[
-            {
-              'mystery_error_rate()': 135,
-            },
-          ]}
+          value={135}
+          field="mystery_error_rate()"
           meta={{
             fields: {
               'mystery_error_rate()': 'rate',

+ 38 - 92
static/app/views/dashboards/widgets/bigNumberWidget/bigNumberWidget.stories.tsx

@@ -26,7 +26,7 @@ export default storyBook(BigNumberWidget, story => {
       <Fragment>
         <p>
           The visualization of <JSXNode name="BigNumberWidget" /> a large number, just
-          like it says on the tin. Depending on the data passed to it, it intelligently
+          like it says on the tin. Depending on the value passed to it, it intelligently
           rounds and humanizes the results. If the number is humanized, hovering over the
           visualization shows a tooltip with the full value.
         </p>
@@ -42,11 +42,8 @@ export default storyBook(BigNumberWidget, story => {
             <BigNumberWidget
               title="EPS"
               description="Number of events per second"
-              data={[
-                {
-                  'eps()': 0.01087819860850493,
-                },
-              ]}
+              value={0.01087819860850493}
+              field="eps()"
               meta={{
                 fields: {
                   'eps()': 'rate',
@@ -67,11 +64,8 @@ export default storyBook(BigNumberWidget, story => {
           <SmallSizingWindow>
             <BigNumberWidget
               title="Count"
-              data={[
-                {
-                  'count()': 178451214,
-                },
-              ]}
+              value={178451214}
+              field="count()"
               meta={{
                 fields: {
                   'count()': 'integer',
@@ -86,11 +80,8 @@ export default storyBook(BigNumberWidget, story => {
             <BigNumberWidget
               title="Query Duration"
               description="p95(span.duration)"
-              data={[
-                {
-                  'p95(span.duration)': 17.28,
-                },
-              ]}
+              value={17.28}
+              field="p95(span.duration)"
               meta={{
                 fields: {
                   'p95(span.duration)': 'duration',
@@ -105,11 +96,8 @@ export default storyBook(BigNumberWidget, story => {
             <BigNumberWidget
               title="Latest Timestamp"
               description=""
-              data={[
-                {
-                  'max(timestamp)': '2024-10-17T16:08:07+00:00',
-                },
-              ]}
+              value={'2024-10-17T16:08:07+00:00'}
+              field="max(timestamp)"
               meta={{
                 fields: {
                   'max(timestamp)': 'date',
@@ -133,11 +121,8 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="Count"
-              data={[
-                {
-                  'count()': 1000000,
-                },
-              ]}
+              value={1000000}
+              field="count()"
               maximumValue={1000000}
               meta={{
                 fields: {
@@ -165,7 +150,7 @@ export default storyBook(BigNumberWidget, story => {
             <BigNumberWidget title="Loading Count" isLoading />
           </SmallWidget>
           <SmallWidget>
-            <BigNumberWidget title="Missing Count" data={[{}]} />
+            <BigNumberWidget title="Missing Count" />
           </SmallWidget>
           <SmallWidget>
             <BigNumberWidget
@@ -201,9 +186,9 @@ export default storyBook(BigNumberWidget, story => {
     return (
       <Fragment>
         <p>
-          <JSXNode name="BigNumberWidget" /> shows the difference of the current data and
-          the previous period data as the difference between the two values, in small text
-          next to the main value.
+          <JSXNode name="BigNumberWidget" /> shows the difference of the current value and
+          the previous period value as the difference between the two values, in small
+          text next to the main value.
         </p>
 
         <p>
@@ -218,16 +203,9 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="eps()"
-              data={[
-                {
-                  'eps()': 17.1087819860850493,
-                },
-              ]}
-              previousPeriodData={[
-                {
-                  'eps()': 15.0088607819850493,
-                },
-              ]}
+              value={17.1087819860850493}
+              field="eps()"
+              previousPeriodValue={15.0088607819850493}
               meta={{
                 fields: {
                   'eps()': 'rate',
@@ -242,16 +220,9 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="http_rate(500)"
-              data={[
-                {
-                  'http_rate(500)': 0.14227123,
-                },
-              ]}
-              previousPeriodData={[
-                {
-                  'http_rate(500)': 0.1728139,
-                },
-              ]}
+              value={0.14227123}
+              previousPeriodValue={0.1728139}
+              field="http_rate(500)"
               preferredPolarity="-"
               meta={{
                 fields: {
@@ -263,16 +234,9 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="http_rate(200)"
-              data={[
-                {
-                  'http_rate(200)': 0.14227123,
-                },
-              ]}
-              previousPeriodData={[
-                {
-                  'http_rate(200)': 0.1728139,
-                },
-              ]}
+              field="http_rate(200)"
+              value={0.14227123}
+              previousPeriodValue={0.1728139}
               preferredPolarity="+"
               meta={{
                 fields: {
@@ -308,19 +272,16 @@ export default storyBook(BigNumberWidget, story => {
       <Fragment>
         <p>
           <JSXNode name="BigNumberWidget" /> supports a <code>thresholds</code> prop. If
-          specified, the value of the data in the widget will be evaluated against these
-          thresholds, and indicated using a colorful circle next to the value.
+          specified, the value in the widget will be evaluated against these thresholds,
+          and indicated using a colorful circle next to the value.
         </p>
 
         <SideBySide>
           <SmallWidget>
             <BigNumberWidget
               title="eps()"
-              data={[
-                {
-                  'eps()': 7.1,
-                },
-              ]}
+              value={7.1}
+              field="eps()"
               meta={meta}
               thresholds={thresholds}
               preferredPolarity="+"
@@ -330,11 +291,8 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="eps()"
-              data={[
-                {
-                  'eps()': 27.781,
-                },
-              ]}
+              value={27.781}
+              field="eps()"
               meta={meta}
               thresholds={thresholds}
               preferredPolarity="-"
@@ -344,11 +302,8 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="eps()"
-              data={[
-                {
-                  'eps()': 78.1,
-                },
-              ]}
+              field="eps()"
+              value={78.1}
               meta={meta}
               thresholds={thresholds}
               preferredPolarity="+"
@@ -365,11 +320,8 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="eps()"
-              data={[
-                {
-                  'eps()': 7.1,
-                },
-              ]}
+              field="eps()"
+              value={7.1}
               meta={meta}
               thresholds={thresholds}
               preferredPolarity="-"
@@ -379,11 +331,8 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="eps()"
-              data={[
-                {
-                  'eps()': 27.781,
-                },
-              ]}
+              field="eps()"
+              value={27.781}
               meta={meta}
               thresholds={thresholds}
               preferredPolarity="-"
@@ -393,11 +342,8 @@ export default storyBook(BigNumberWidget, story => {
           <SmallWidget>
             <BigNumberWidget
               title="eps()"
-              data={[
-                {
-                  'eps()': 78.1,
-                },
-              ]}
+              field="eps()"
+              value={78.1}
               meta={meta}
               thresholds={thresholds}
               preferredPolarity="-"

+ 6 - 12
static/app/views/dashboards/widgets/bigNumberWidget/bigNumberWidget.tsx

@@ -11,29 +11,23 @@ import {
 } from 'sentry/views/dashboards/widgets/common/widgetFrame';
 
 import {
+  DEFAULT_FIELD,
   MISSING_DATA_MESSAGE,
   NON_FINITE_NUMBER_MESSAGE,
   X_GUTTER,
   Y_GUTTER,
 } from '../common/settings';
-import type {DataProps, StateProps} from '../common/types';
+import type {StateProps} from '../common/types';
 
 import {DEEMPHASIS_COLOR_NAME, LOADING_PLACEHOLDER} from './settings';
 
 interface Props
-  extends DataProps,
-    StateProps,
+  extends StateProps,
     Omit<WidgetFrameProps, 'children'>,
-    Omit<BigNumberWidgetVisualizationProps, 'value' | 'previousPeriodValue'> {}
+    Partial<BigNumberWidgetVisualizationProps> {}
 
 export function BigNumberWidget(props: Props) {
-  const {data, previousPeriodData} = props;
-
-  // TODO: Instrument getting more than one data key back as an error
-  // e.g., with data that looks like `[{'apdex()': 0.8}] this pulls out `"apdex()"` or `undefined`
-  const field = Object.keys(data?.[0] ?? {})[0];
-  const value = data?.[0]?.[field];
-  const previousPeriodValue = previousPeriodData?.[0]?.[field];
+  const {value, previousPeriodValue, field} = props;
 
   if (props.isLoading) {
     return (
@@ -69,7 +63,7 @@ export function BigNumberWidget(props: Props) {
           <BigNumberWidgetVisualization
             value={value}
             previousPeriodValue={previousPeriodValue}
-            field={field}
+            field={field ?? DEFAULT_FIELD}
             maximumValue={props.maximumValue}
             preferredPolarity={props.preferredPolarity}
             meta={props.meta}

+ 6 - 7
static/app/views/dashboards/widgets/bigNumberWidget/bigNumberWidgetVisualization.tsx

@@ -15,13 +15,13 @@ import type {
   Thresholds,
 } from 'sentry/views/dashboards/widgets/common/types';
 
-import {DEFAULT_FIELD, X_GUTTER, Y_GUTTER} from '../common/settings';
+import {X_GUTTER, Y_GUTTER} from '../common/settings';
 
 import {ThresholdsIndicator} from './thresholdsIndicator';
 
 export interface BigNumberWidgetVisualizationProps {
+  field: string;
   value: number | string;
-  field?: string;
   maximumValue?: number;
   meta?: Meta;
   preferredPolarity?: Polarity;
@@ -31,7 +31,7 @@ export interface BigNumberWidgetVisualizationProps {
 
 export function BigNumberWidgetVisualization(props: BigNumberWidgetVisualizationProps) {
   const {
-    field = DEFAULT_FIELD,
+    field,
     value,
     previousPeriodValue,
     maximumValue = Number.MAX_VALUE,
@@ -43,10 +43,9 @@ export function BigNumberWidgetVisualization(props: BigNumberWidgetVisualization
   const organization = useOrganization();
 
   // TODO: meta as MetaType is a white lie. `MetaType` doesn't know that types can be null, but they can!
-  const fieldRenderer =
-    meta && field
-      ? getFieldRenderer(field, meta as MetaType, false)
-      : renderableValue => renderableValue.toString();
+  const fieldRenderer = meta
+    ? getFieldRenderer(field, meta as MetaType, false)
+    : renderableValue => renderableValue.toString();
 
   const unit = meta?.units?.[field];
   const type = meta?.fields?.[field];

+ 0 - 5
static/app/views/dashboards/widgets/common/types.tsx

@@ -8,11 +8,6 @@ export type Meta = {
 type TableRow = Record<string, number | string | undefined>;
 export type TableData = TableRow[];
 
-export interface DataProps {
-  data?: TableData;
-  previousPeriodData?: TableData;
-}
-
 export type ErrorProp = Error | string;
 
 export interface StateProps {

+ 3 - 10
static/app/views/projectDetail/projectScoreCards/projectAnrScoreCard.tsx

@@ -142,16 +142,9 @@ export function ProjectAnrScoreCard({
     <BigNumberWidget
       title={t('ANR Rate')}
       description={getSessionTermDescription(SessionTerm.ANR_RATE, null)}
-      data={[
-        {
-          'anr_rate()': value ?? undefined,
-        },
-      ]}
-      previousPeriodData={[
-        {
-          'anr_rate()': previousValue ?? undefined,
-        },
-      ]}
+      value={value ?? undefined}
+      previousPeriodValue={previousValue ?? undefined}
+      field="anr_rate()"
       preferredPolarity="-"
       meta={{
         fields: {

+ 3 - 10
static/app/views/projectDetail/projectScoreCards/projectApdexScoreCard.tsx

@@ -123,16 +123,9 @@ function ProjectApdexScoreCard(props: Props) {
     <BigNumberWidget
       title={cardTitle}
       description={cardHelp}
-      data={[
-        {
-          'apdex()': apdex,
-        },
-      ]}
-      previousPeriodData={[
-        {
-          'apdex()': previousApdex,
-        },
-      ]}
+      value={apdex}
+      previousPeriodValue={previousApdex}
+      field="apdex()"
       meta={{
         fields: {
           'apdex()': 'number',

+ 3 - 10
static/app/views/projectDetail/projectScoreCards/projectStabilityScoreCard.tsx

@@ -148,16 +148,9 @@ function ProjectStabilityScoreCard(props: Props) {
     <BigNumberWidget
       title={cardTitle}
       description={cardHelp}
-      data={[
-        {
-          [`${props.field}()`]: score ? score / 100 : undefined,
-        },
-      ]}
-      previousPeriodData={[
-        {
-          [`${props.field}()`]: previousScore ? previousScore / 100 : undefined,
-        },
-      ]}
+      value={score ? score / 100 : undefined}
+      previousPeriodValue={previousScore ? previousScore / 100 : undefined}
+      field={`${props.field}()`}
       meta={{
         fields: {
           [`${props.field}()`]: 'percentage',

+ 3 - 10
static/app/views/projectDetail/projectScoreCards/projectVelocityScoreCard.tsx

@@ -156,16 +156,9 @@ function ProjectVelocityScoreCard(props: Props) {
     <BigNumberWidget
       title={cardTitle}
       description={cardHelp}
-      data={[
-        {
-          'count()': currentReleases?.length,
-        },
-      ]}
-      previousPeriodData={[
-        {
-          'count()': previousReleases?.length,
-        },
-      ]}
+      value={currentReleases?.length}
+      previousPeriodValue={previousReleases?.length}
+      field="count()"
       maximumValue={API_LIMIT}
       meta={{
         fields: {