diff --git a/apps/web/src/components/features/layouts/AppVerticalCodeSplitter/DataContainer.stories.tsx b/apps/web/src/components/features/layouts/AppVerticalCodeSplitter/DataContainer.stories.tsx new file mode 100644 index 000000000..05a40ef49 --- /dev/null +++ b/apps/web/src/components/features/layouts/AppVerticalCodeSplitter/DataContainer.stories.tsx @@ -0,0 +1,65 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { faker } from '@faker-js/faker'; +import { DataContainer } from './DataContainer'; +import type { DataResult } from '@buster/server-shared/metrics'; + +// Generate mock data that would be compatible with AppDataGrid +const generateMockData = (): DataResult => { + const columns = ['id', 'name', 'email', 'department', 'salary', 'join_date']; + const rows = Array.from({ length: 50 }, (_, index) => ({ + id: faker.string.uuid(), + name: faker.person.fullName(), + email: index % 3 === 2 ? null : faker.internet.email(), + department: faker.commerce.department(), + salary: faker.number.int({ min: 40000, max: 150000 }), + join_date: faker.date.past({ years: 5 }).toISOString().split('T')[0] + })); + + return rows as DataResult; +}; + +const meta = { + title: 'Features/Layouts/DataContainer', + component: DataContainer, + parameters: { + layout: 'fullscreen' + }, + decorators: [ + (Story) => ( +
+ +
+ ) + ] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const WithData: Story = { + args: { + data: generateMockData(), + fetchingData: false + } +}; + +export const Loading: Story = { + args: { + data: null, + fetchingData: true + } +}; + +export const LoadingWithData: Story = { + args: { + data: generateMockData(), + fetchingData: true + } +}; + +export const NoData: Story = { + args: { + data: null, + fetchingData: false + } +}; diff --git a/apps/web/src/components/ui/charts/stories/BusterTableChart.stories.tsx b/apps/web/src/components/ui/charts/stories/BusterTableChart.stories.tsx index 0b29c6de8..e13200c60 100644 --- a/apps/web/src/components/ui/charts/stories/BusterTableChart.stories.tsx +++ b/apps/web/src/components/ui/charts/stories/BusterTableChart.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; import { BusterTableChart } from '../TableChart/BusterTableChart'; -import type { ColumnLabelFormat } from '@buster/server-shared/metrics'; +import { DEFAULT_COLUMN_LABEL_FORMAT, type ColumnLabelFormat } from '@buster/server-shared/metrics'; // Helper functions for generating sample data const generateProductName = (index: number) => `Product ${index + 1}`; @@ -281,3 +281,19 @@ export const CustomClassname: Story = { ); } }; + +export const WithNullStringValues: Story = { + args: { + data: generateTableData().map((item, index) => ({ + ...item, + product: index % 2 === 0 ? null : item.product + })), + columnLabelFormats: { + product: { + ...DEFAULT_COLUMN_LABEL_FORMAT, + columnType: 'text', + style: 'string' + } as ColumnLabelFormat + } + } +}; diff --git a/apps/web/src/lib/columnFormatter.test.ts b/apps/web/src/lib/columnFormatter.test.ts index 715a81b32..3f2cb7af0 100644 --- a/apps/web/src/lib/columnFormatter.test.ts +++ b/apps/web/src/lib/columnFormatter.test.ts @@ -243,7 +243,7 @@ describe('formatLabel', () => { style: 'string', replaceMissingDataWith: 0 }) - ).toBe('0'); + ).toBe('null'); }); it('should handle replaceMissingDataWith for undefined values with string', () => { @@ -351,5 +351,11 @@ describe('formatLabel', () => { formatLabel(null, { columnType: 'date', style: 'date', replaceMissingDataWith: 'N/A' }) ).toBe('N/A'); }); + + it('should not allow replaceMissingDataWith to be a number when columnType is text', () => { + expect( + formatLabel(null, { columnType: 'text', style: 'string', replaceMissingDataWith: 0 }) + ).toBe('null'); + }); }); }); diff --git a/apps/web/src/lib/columnFormatter.ts b/apps/web/src/lib/columnFormatter.ts index ce1497298..abecd63b6 100644 --- a/apps/web/src/lib/columnFormatter.ts +++ b/apps/web/src/lib/columnFormatter.ts @@ -57,9 +57,17 @@ export const formatLabel = ( replaceMissingDataWith ?? DEFAULT_COLUMN_LABEL_FORMAT.replaceMissingDataWith ); } - } else if (replaceMissingDataWith !== undefined) { - formattedText = String(replaceMissingDataWith); - } else formattedText = String('null'); + } + // I removed this because it was causing issues with null values and strings... + else if (replaceMissingDataWith !== undefined) { + if (columnType === 'text' && typeof replaceMissingDataWith !== 'number') { + formattedText = String(replaceMissingDataWith); + } else if (columnType !== 'text') { + formattedText = String(replaceMissingDataWith); + } + } else { + formattedText = String('null'); + } } else if (style === 'date' && !useKeyFormatter) { formattedText = formatLabelDate(text as string | number | Date, { dateFormat,