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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user