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:
@ -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 =
|
||||
|
||||
@ -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');
|
||||
},
|
||||
};
|
||||
@ -64,7 +64,7 @@ export const RecordTable = () => {
|
||||
tableBodyRef={tableBodyRef}
|
||||
/>
|
||||
|
||||
{recordTableIsEmpty ? (
|
||||
{recordTableIsEmpty && !hasRecordGroups ? (
|
||||
<RecordTableEmpty
|
||||
tableBodyRef={tableBodyRef}
|
||||
hasRecordGroups={hasRecordGroups}
|
||||
|
||||
@ -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 />
|
||||
</>
|
||||
);
|
||||
|
||||
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -12,6 +12,7 @@ const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
min-height: 100px;
|
||||
`;
|
||||
|
||||
interface StyledImageProps {
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -28,6 +28,7 @@ export const ComponentDecorator: Decorator = (Story, context) => {
|
||||
return (
|
||||
<ComponentStorybookLayout
|
||||
width={container?.width}
|
||||
height={container?.height}
|
||||
backgroundColor={backgroundColor}
|
||||
>
|
||||
<Story />
|
||||
|
||||
Reference in New Issue
Block a user