Fix aggregate bar (#11620)

Closes https://github.com/twentyhq/twenty/issues/10943

Also adds stories:
<img width="1512" alt="image"
src="https://github.com/user-attachments/assets/377059b1-f6b5-4d8c-b7d1-e74e70448445"
/>
This commit is contained in:
Charles Bochet
2025-04-17 15:29:20 +02:00
committed by GitHub
parent d2881bb4a2
commit 56874bf84b
14 changed files with 339 additions and 98 deletions

View File

@ -23,8 +23,8 @@ import { ViewField } from '@/views/types/ViewField';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isDefined } from 'twenty-shared/utils';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useLoadRecordIndexStates = () => {
const setContextStoreTargetedRecordsRuleComponentState =

View File

@ -0,0 +1,117 @@
import { Meta, StoryObj } from '@storybook/react';
import { RecordTableWithWrappers } from '@/object-record/record-table/components/RecordTableWithWrappers';
import { RecordTableEmptyStateNoGroupNoRecordAtAll } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll';
import { fireEvent, userEvent, within } from '@storybook/test';
import { ComponentDecorator } from 'twenty-ui/testing';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { RecordTableDecorator } from '~/testing/decorators/RecordTableDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { mockedViewsData } from '~/testing/mock-data/views';
import { sleep } from '~/utils/sleep';
const meta: Meta = {
title: 'Modules/ObjectRecord/RecordTable/RecordTable',
component: RecordTableWithWrappers,
decorators: [
ComponentDecorator,
MemoryRouterDecorator,
RecordTableDecorator,
ContextStoreDecorator,
SnackBarDecorator,
ObjectMetadataItemsDecorator,
I18nFrontDecorator,
],
args: {
recordTableId: `companies-${mockedViewsData[0].id}`,
viewBarId: 'view-bar',
objectNameSingular: 'company',
},
parameters: {
recordTableObjectNameSingular: 'company',
msw: graphqlMocks,
},
};
export default meta;
type Story = StoryObj<typeof RecordTableEmptyStateNoGroupNoRecordAtAll>;
export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByText('Linkedin');
},
};
export const HeaderMenuOpen: Story = {
play: async () => {
const canvas = within(document.body);
await canvas.findByText('Linkedin');
const headerMenuButton = await canvas.findByText('Domain Name');
await userEvent.click(headerMenuButton);
await canvas.findByText('Move right');
},
};
export const ScrolledLeft: Story = {
play: async () => {
const canvas = within(document.body);
await canvas.findByText('Linkedin');
const scrollWrapper = document.body.querySelector(
'.scroll-wrapper-x-enabled',
);
if (!scrollWrapper) {
throw new Error('Scroll wrapper not found');
}
await sleep(1000);
fireEvent.scroll(scrollWrapper, {
target: {
scrollLeft: 100,
},
});
await canvas.findByText('Facebook');
},
};
export const ScrolledBottom: Story = {
parameters: {
container: {
height: 300,
},
},
play: async () => {
const canvas = within(document.body);
await canvas.findByText('Linkedin');
const scrollWrapper = document.body.querySelector(
'.scroll-wrapper-y-enabled',
);
if (!scrollWrapper) {
throw new Error('Scroll wrapper not found');
}
await sleep(1000);
fireEvent.scroll(scrollWrapper, {
target: {
scrollTop: 80,
},
});
await canvas.findByText('Facebook');
},
};

View File

@ -64,7 +64,7 @@ export const RecordTable = () => {
tableBodyRef={tableBodyRef}
/>
{recordTableIsEmpty ? (
{recordTableIsEmpty && !hasRecordGroups ? (
<RecordTableEmpty
tableBodyRef={tableBodyRef}
hasRecordGroups={hasRecordGroups}

View File

@ -1,6 +1,5 @@
import { StyledTable } from '@/object-record/record-table/components/RecordTableStyles';
import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState';
import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody';
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
export interface RecordTableEmptyProps {
@ -8,18 +7,11 @@ export interface RecordTableEmptyProps {
hasRecordGroups: boolean;
}
export const RecordTableEmpty = ({
tableBodyRef,
hasRecordGroups,
}: RecordTableEmptyProps) => (
export const RecordTableEmpty = ({ tableBodyRef }: RecordTableEmptyProps) => (
<>
<StyledTable ref={tableBodyRef}>
<RecordTableHeader />
</StyledTable>
{hasRecordGroups ? (
<RecordTableRecordGroupsBody />
) : (
<RecordTableEmptyState />
)}
<RecordTableEmptyState />
</>
);

View File

@ -1,35 +1,40 @@
import { Meta, StoryObj } from '@storybook/react';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
import { RecordTableEmptyStateNoGroupNoRecordAtAll } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateNoGroupNoRecordAtAll';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { ComponentDecorator } from 'twenty-ui/testing';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { RecordTableDecorator } from '~/testing/decorators/RecordTableDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { ComponentDecorator } from 'twenty-ui/testing';
const meta: Meta = {
title:
'Modules/ObjectRecord/RecordTable/RecordTableEmptyStateNoGroupNoRecordAtAll',
component: RecordTableEmptyStateNoGroupNoRecordAtAll,
decorators: [
(Story) => (
<RecordTableContextProvider
recordTableId="persons"
viewBarId="view-bar"
objectNameSingular="person"
>
<Story />
</RecordTableContextProvider>
),
ComponentDecorator,
MemoryRouterDecorator,
ObjectMetadataItemsDecorator,
RecordTableDecorator,
(Story) => (
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
<RecordTableComponentInstance
recordTableId="persons"
onColumnsChange={() => {}}
>
<Story />
</RecordTableComponentInstance>
</SnackBarProviderScope>
),
ContextStoreDecorator,
SnackBarDecorator,
ObjectMetadataItemsDecorator,
I18nFrontDecorator,
],
parameters: {
recordTableObjectNameSingular: 'person',
msw: graphqlMocks,
},
};

View File

@ -1,35 +1,40 @@
import { Meta, StoryObj } from '@storybook/react';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
import { RecordTableEmptyStateNoRecordFoundForFilter } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateNoRecordFoundForFilter';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { ComponentDecorator } from 'twenty-ui/testing';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { RecordTableDecorator } from '~/testing/decorators/RecordTableDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { ComponentDecorator } from 'twenty-ui/testing';
const meta: Meta = {
title:
'Modules/ObjectRecord/RecordTable/RecordTableEmptyStateNoRecordFoundForFilter',
component: RecordTableEmptyStateNoRecordFoundForFilter,
decorators: [
(Story) => (
<RecordTableContextProvider
recordTableId="persons"
viewBarId="view-bar"
objectNameSingular="person"
>
<Story />
</RecordTableContextProvider>
),
ComponentDecorator,
MemoryRouterDecorator,
ObjectMetadataItemsDecorator,
RecordTableDecorator,
(Story) => (
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
<RecordTableComponentInstance
recordTableId="persons"
onColumnsChange={() => {}}
>
<Story />
</RecordTableComponentInstance>
</SnackBarProviderScope>
),
ContextStoreDecorator,
SnackBarDecorator,
ObjectMetadataItemsDecorator,
I18nFrontDecorator,
],
parameters: {
recordTableObjectNameSingular: 'person',
msw: graphqlMocks,
},
};

View File

@ -1,34 +1,39 @@
import { Meta, StoryObj } from '@storybook/react';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
import { RecordTableEmptyStateRemote } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateRemote';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { ComponentDecorator } from 'twenty-ui/testing';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { RecordTableDecorator } from '~/testing/decorators/RecordTableDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { ComponentDecorator } from 'twenty-ui/testing';
const meta: Meta = {
title: 'Modules/ObjectRecord/RecordTable/RecordTableEmptyStateRemote',
component: RecordTableEmptyStateRemote,
decorators: [
(Story) => (
<RecordTableContextProvider
recordTableId="persons"
viewBarId="view-bar"
objectNameSingular="person"
>
<Story />
</RecordTableContextProvider>
),
ComponentDecorator,
MemoryRouterDecorator,
ObjectMetadataItemsDecorator,
RecordTableDecorator,
(Story) => (
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
<RecordTableComponentInstance
recordTableId="persons"
onColumnsChange={() => {}}
>
<Story />
</RecordTableComponentInstance>
</SnackBarProviderScope>
),
ContextStoreDecorator,
SnackBarDecorator,
ObjectMetadataItemsDecorator,
I18nFrontDecorator,
],
parameters: {
recordTableObjectNameSingular: 'person',
msw: graphqlMocks,
},
};

View File

@ -1,39 +1,39 @@
import { Meta, StoryObj } from '@storybook/react';
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance';
import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider';
import { RecordTableEmptyStateSoftDelete } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete';
import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope';
import { ComponentDecorator } from 'twenty-ui/testing';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { MemoryRouterDecorator } from '~/testing/decorators/MemoryRouterDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { RecordTableDecorator } from '~/testing/decorators/RecordTableDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { ComponentDecorator } from 'twenty-ui/testing';
const meta: Meta = {
title: 'Modules/ObjectRecord/RecordTable/RecordTableEmptyStateSoftDelete',
component: RecordTableEmptyStateSoftDelete,
decorators: [
(Story) => (
<RecordTableContextProvider
recordTableId="persons"
viewBarId="view-bar"
objectNameSingular="person"
>
<Story />
</RecordTableContextProvider>
),
ComponentDecorator,
MemoryRouterDecorator,
ObjectMetadataItemsDecorator,
RecordTableDecorator,
(Story) => (
<SnackBarProviderScope snackBarManagerScopeId="snack-bar-manager">
<RecordFiltersComponentInstanceContext.Provider
value={{ instanceId: 'record-filters-component-instance' }}
>
<RecordTableComponentInstance
recordTableId="persons"
onColumnsChange={() => {}}
>
<Story />
</RecordTableComponentInstance>
</RecordFiltersComponentInstanceContext.Provider>
</SnackBarProviderScope>
),
ContextStoreDecorator,
SnackBarDecorator,
ObjectMetadataItemsDecorator,
I18nFrontDecorator,
],
parameters: {
recordTableObjectNameSingular: 'person',
msw: graphqlMocks,
},
};

View File

@ -5,20 +5,20 @@ const StyledTbody = styled.tbody`
td:nth-of-type(1) {
position: sticky;
left: 0;
z-index: 5;
z-index: 6;
transition: 0.3s ease;
}
td:nth-of-type(2) {
position: sticky;
left: 11px;
z-index: 5;
z-index: 6;
transition: 0.3s ease;
}
tr:not(:last-child) td:nth-of-type(3) {
// Last row is aggregate footer
position: sticky;
left: 43px;
z-index: 5;
z-index: 6;
transition: 0.3s ease;
&:not(.disable-shadow)::after {

View File

@ -26,21 +26,21 @@ const StyledTableHead = styled.thead`
th:nth-of-type(1) {
position: sticky;
left: 0;
z-index: 5;
z-index: 6;
transition: 0.3s ease;
}
th:nth-of-type(2) {
position: sticky;
left: 11px;
z-index: 5;
z-index: 6;
transition: 0.3s ease;
}
th:nth-of-type(3) {
position: sticky;
left: 43px;
z-index: 5;
z-index: 6;
transition: 0.3s ease;
&::after {
@ -65,7 +65,7 @@ const StyledTableHead = styled.thead`
th {
position: sticky;
top: 0;
z-index: 5;
z-index: 6;
}
}

View File

@ -1,41 +1,81 @@
import { Decorator } from '@storybook/react';
import { useRecoilValue } from 'recoil';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/record-filter-group/states/context/RecordFilterGroupsComponentInstanceContext';
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useLoadRecordIndexStates } from '@/object-record/record-index/hooks/useLoadRecordIndexStates';
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-store/contexts/RecordFieldValueSelectorContext';
import { RecordTableBodyContextProvider } from '@/object-record/record-table/contexts/RecordTableBodyContext';
import { RecordTableContextProvider } from '@/object-record/record-table/contexts/RecordTableContext';
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { View } from '@/views/types/View';
import { useEffect, useMemo } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { mockedViewFieldsData } from '~/testing/mock-data/view-fields';
import { mockedViewsData } from '~/testing/mock-data/views';
export const RecordTableDecorator: Decorator = (Story) => {
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const InternalTableStateLoaderEffect = ({
objectMetadataItem,
}: {
objectMetadataItem: ObjectMetadataItem;
}) => {
const { loadRecordIndexStates } = useLoadRecordIndexStates();
const personObjectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) => objectMetadataItem.nameSingular === 'person',
const view = useMemo(() => {
return {
...mockedViewsData[0],
viewFields: mockedViewFieldsData.filter(
(viewField) => viewField.viewId === mockedViewsData[0].id,
),
} as unknown as View;
}, []);
useEffect(() => {
loadRecordIndexStates(view, objectMetadataItem);
}, [loadRecordIndexStates, objectMetadataItem, view]);
return null;
};
const InternalTableContextProviders = ({
children,
objectMetadataItem,
}: {
children: React.ReactNode;
objectMetadataItem: ObjectMetadataItem;
}) => {
const visibleTableColumns = useRecoilComponentValueV2(
visibleTableColumnsComponentSelector,
);
if (!isDefined(personObjectMetadataItem)) {
return <Story />;
}
return (
<RecordIndexContextProvider
value={{
indexIdentifierUrl: () => '',
onIndexRecordsLoaded: () => {},
objectNamePlural: personObjectMetadataItem.namePlural,
objectNameSingular: personObjectMetadataItem.nameSingular,
objectMetadataItem: personObjectMetadataItem,
objectNamePlural: objectMetadataItem.namePlural,
objectNameSingular: objectMetadataItem.nameSingular,
objectMetadataItem: objectMetadataItem,
recordIndexId: 'record-index',
}}
>
<RecordTableContextProvider
value={{
objectNameSingular: personObjectMetadataItem.nameSingular,
objectMetadataItem: personObjectMetadataItem,
recordTableId: 'persons',
objectNameSingular: objectMetadataItem.nameSingular,
objectMetadataItem: objectMetadataItem,
recordTableId: objectMetadataItem.namePlural,
viewBarId: 'view-bar',
visibleTableColumns: [],
visibleTableColumns: visibleTableColumns,
}}
>
<RecordTableBodyContextProvider
@ -48,9 +88,71 @@ export const RecordTableDecorator: Decorator = (Story) => {
onMoveSoftFocusToCurrentCell: () => {},
}}
>
<Story />
{children}
</RecordTableBodyContextProvider>
</RecordTableContextProvider>
</RecordIndexContextProvider>
);
};
export const RecordTableDecorator: Decorator = (Story, context) => {
const { recordTableObjectNameSingular: objectNameSingular } =
context.parameters;
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
const objectMetadataItem = objectMetadataItems.find(
(objectMetadataItem) =>
objectMetadataItem.nameSingular === objectNameSingular,
);
if (!isDefined(objectMetadataItem)) {
throw new Error(
'Object metadata item not found while loading RecordTableDecorator',
);
}
const recordIndexId = getRecordIndexIdFromObjectNamePluralAndViewId(
objectMetadataItem.namePlural,
mockedViewsData[0].id,
);
return (
<RecordFieldValueSelectorContextProvider>
<RecordTableComponentInstanceContext.Provider
value={{ instanceId: recordIndexId, onColumnsChange: () => {} }}
>
<ViewComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }}
>
<RecordFilterGroupsComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }}
>
<RecordFiltersComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }}
>
<RecordSortsComponentInstanceContext.Provider
value={{ instanceId: recordIndexId }}
>
<ActionMenuComponentInstanceContext.Provider
value={{
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
}}
>
<InternalTableStateLoaderEffect
objectMetadataItem={objectMetadataItem}
/>
<InternalTableContextProviders
objectMetadataItem={objectMetadataItem}
>
<Story />
</InternalTableContextProviders>
</ActionMenuComponentInstanceContext.Provider>
</RecordSortsComponentInstanceContext.Provider>
</RecordFiltersComponentInstanceContext.Provider>
</RecordFilterGroupsComponentInstanceContext.Provider>
</ViewComponentInstanceContext.Provider>
</RecordTableComponentInstanceContext.Provider>
</RecordFieldValueSelectorContextProvider>
);
};

View File

@ -12,6 +12,7 @@ const StyledContainer = styled.div`
align-items: center;
justify-content: center;
position: relative;
min-height: 100px;
`;
interface StyledImageProps {

View File

@ -1,8 +1,10 @@
import styled from '@emotion/styled';
import { isDefined } from 'twenty-shared/utils';
const StyledLayout = styled.div<{
width?: number;
backgroundColor?: string | undefined;
height: number | 'fit-content';
}>`
background: ${({ theme, backgroundColor }) =>
backgroundColor ?? theme.background.primary};
@ -12,7 +14,12 @@ const StyledLayout = styled.div<{
display: flex;
flex-direction: row;
height: fit-content;
height: ${({ height }) =>
height === 'fit-content'
? 'fit-content'
: `
${height}px
`};
max-width: calc(100% - 40px);
min-width: ${({ width }) => (width ? 'unset' : '300px')};
padding: 20px;
@ -22,15 +29,21 @@ const StyledLayout = styled.div<{
type ComponentStorybookLayoutProps = {
width?: number;
backgroundColor?: string | undefined;
height?: number;
children: JSX.Element;
};
export const ComponentStorybookLayout = ({
width,
backgroundColor,
height,
children,
}: ComponentStorybookLayoutProps) => (
<StyledLayout width={width} backgroundColor={backgroundColor}>
<StyledLayout
width={width}
backgroundColor={backgroundColor}
height={isDefined(height) ? height : 'fit-content'}
>
{children}
</StyledLayout>
);

View File

@ -28,6 +28,7 @@ export const ComponentDecorator: Decorator = (Story, context) => {
return (
<ComponentStorybookLayout
width={container?.width}
height={container?.height}
backgroundColor={backgroundColor}
>
<Story />