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,