Migrate to twenty-ui - display (#8004)

This PR was created by [GitStart](https://gitstart.com/) to address the
requirements from this ticket:
[TWNTY-6871](https://clients.gitstart.com/twenty/5449/tickets/TWNTY-6871).

 --- 

### Description

Migrate:

- Info display component
- Status display component
- SeparatorLineText display component

### Demo

###### SeparatorLineText In Storybook


![](https://assets-service.gitstart.com/4814/c0a2cd49-e545-469a-b3d3-c02eb462b60d.png)

Info Component on Storybook


![](https://assets-service.gitstart.com/4814/6f3019c5-99e0-4365-a81e-241294887f9e.png)

Status Component on Storybook


![](https://assets-service.gitstart.com/4814/29b5142a-468f-4d7e-88ff-4f3bfdd5abda.png)

###### Fixes twentyhq/private-issues#95

---------

Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
gitstart-app[bot]
2024-10-24 17:50:14 +02:00
committed by GitHub
parent e44d525e83
commit b09ecfbb8c
45 changed files with 133 additions and 110 deletions

View File

@ -10,12 +10,12 @@ import { previousUrlState } from '@/auth/states/previousUrlState';
import { tokenPairState } from '@/auth/states/tokenPairState';
import { workspacesState } from '@/auth/states/workspaces';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { AppPath } from '@/types/AppPath';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { useUpdateEffect } from '~/hooks/useUpdateEffect';
import { isDefined } from '~/utils/isDefined';
import { AppPath } from '@/types/AppPath';
import { ApolloFactory, Options } from '../services/apollo.factory';
export const useApolloFactory = (options: Partial<Options<any>> = {}) => {

View File

@ -1,7 +1,6 @@
import { useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { IconCheckbox } from 'twenty-ui';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import {
@ -21,6 +20,7 @@ import { AppPath } from '@/types/AppPath';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { SettingsPath } from '@/types/SettingsPath';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { IconCheckbox } from 'twenty-ui';
import { useCleanRecoilState } from '~/hooks/useCleanRecoilState';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';

View File

@ -5,20 +5,20 @@ import { useParams, useSearchParams } from 'react-router-dom';
import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm';
import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken';
import { useRequestFreshCaptchaToken } from '@/captcha/hooks/useRequestFreshCaptchaToken';
import { AppPath } from '@/types/AppPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { isDefined } from '~/utils/isDefined';
import { useAuth } from '../../hooks/useAuth';
import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
import { availableSSOIdentityProvidersState } from '@/auth/states/availableWorkspacesForSSO';
import {
SignInUpStep,
signInUpStepState,
} from '@/auth/states/signInUpStepState';
import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
import { availableSSOIdentityProvidersState } from '@/auth/states/availableWorkspacesForSSO';
import { AppPath } from '@/types/AppPath';
import { useAuth } from '../../hooks/useAuth';
export enum SignInUpMode {
SignIn = 'sign-in',

View File

@ -3,11 +3,12 @@ import { useNavigate, useParams } from 'react-router-dom';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { AppPath } from '@/types/AppPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState';
import { AppPath } from '@/types/AppPath';
import { useGetWorkspaceFromInviteHashQuery } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';

View File

@ -4,13 +4,12 @@ import { MemoryRouter } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
import { currentMobileNavigationDrawerState } from '@/navigation/states/currentMobileNavigationDrawerState';
import { AppPath } from '@/types/AppPath';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { AppPath } from '@/types/AppPath';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import {
AppNavigationDrawer,

View File

@ -1,5 +1,4 @@
import { AppPath } from '@/types/AppPath';
import indexAppPath from '../indexAppPath';
describe('getIndexAppPath', () => {

View File

@ -2,7 +2,7 @@ import { ConnectedAccount } from '@/accounts/types/ConnectedAccount';
import { SettingsAccountsRowDropdownMenu } from '@/settings/accounts/components/SettingsAccountsRowDropdownMenu';
import { SyncStatus } from '@/settings/accounts/constants/SyncStatus';
import { computeSyncStatus } from '@/settings/accounts/utils/computeSyncStatus';
import { Status } from '@/ui/display/status/components/Status';
import { Status } from 'twenty-ui';
import styled from '@emotion/styled';
const StyledRowRightContainer = styled.div`

View File

@ -1,6 +1,6 @@
import { AppPath } from '@/types/AppPath';
import { useCallback } from 'react';
import { AppPath } from '@/types/AppPath';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import {
CalendarChannelVisibility,

View File

@ -1,10 +1,17 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { Link } from 'react-router-dom';
import { Button, IconArrowUpRight, IconBolt, IconPlus, Pill } from 'twenty-ui';
import {
Button,
IconArrowUpRight,
IconBolt,
IconPlus,
Pill,
Status,
} from 'twenty-ui';
import { SettingsIntegration } from '@/settings/integrations/types/SettingsIntegration';
import { Status } from '@/ui/display/status/components/Status';
import { Link } from 'react-router-dom';
import { isDefined } from '~/utils/isDefined';
interface SettingsIntegrationComponentProps {

View File

@ -1,5 +1,5 @@
import { useGetDatabaseConnectionTables } from '@/databases/hooks/useGetDatabaseConnectionTables';
import { Status } from '@/ui/display/status/components/Status';
import { Status } from 'twenty-ui';
import { RemoteTableStatus } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';

View File

@ -10,7 +10,6 @@ import {
import { SettingsIntegration } from '@/settings/integrations/types/SettingsIntegration';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
import { SettingsPath } from '@/types/SettingsPath';
import { Info } from '@/ui/display/info/components/Info';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { zodResolver } from '@hookform/resolvers/zod';
@ -18,7 +17,7 @@ import { Section } from '@react-email/components';
import pick from 'lodash.pick';
import { FormProvider, useForm } from 'react-hook-form';
import { useNavigate } from 'react-router-dom';
import { Breadcrumb, H2Title } from 'twenty-ui';
import { Breadcrumb, H2Title, Info } from 'twenty-ui';
import { z } from 'zod';
import {
RemoteServer,

View File

@ -1,6 +1,6 @@
import { WatchQueryFetchPolicy } from '@apollo/client';
import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { WatchQueryFetchPolicy } from '@apollo/client';
import { useGetDatabaseConnection } from '@/databases/hooks/useGetDatabaseConnection';
import { useGetDatabaseConnectionTables } from '@/databases/hooks/useGetDatabaseConnectionTables';

View File

@ -3,7 +3,7 @@
import { SettingsSecuritySSORowDropdownMenu } from '@/settings/security/components/SettingsSecuritySSORowDropdownMenu';
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProviders.state';
import { getColorBySSOIdentityProviderStatus } from '@/settings/security/utils/getColorBySSOIdentityProviderStatus';
import { Status } from '@/ui/display/status/components/Status';
import { Status } from 'twenty-ui';
import styled from '@emotion/styled';
import { UnwrapRecoilValue } from 'recoil';

View File

@ -1,92 +0,0 @@
import { css, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import React from 'react';
import { Link } from 'react-router-dom';
import { Button, IconInfoCircle } from 'twenty-ui';
import { AppPath } from '@/types/AppPath';
export type InfoAccent = 'blue' | 'danger';
export type InfoProps = {
accent?: InfoAccent;
text: string;
buttonTitle?: string;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
to?: AppPath;
};
const StyledTextContainer = styled.div`
align-items: center;
display: flex;
gap: ${({ theme }) => theme.spacing(2)};
`;
const StyledIconInfoCircle = styled(IconInfoCircle)`
flex-shrink: 0;
`;
const StyledInfo = styled.div<Pick<InfoProps, 'accent'>>`
align-items: center;
border-radius: ${({ theme }) => theme.border.radius.md};
display: flex;
font-weight: ${({ theme }) => theme.font.weight.medium};
justify-content: space-between;
max-width: 512px;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)};
${({ theme, accent }) => {
switch (accent) {
case 'blue':
return css`
background: ${theme.color.blueAccent20};
color: ${theme.color.blue50};
`;
case 'danger':
return css`
background: ${theme.color.red10};
color: ${theme.color.red};
`;
}
}}
`;
const StyledLink = styled(Link)`
text-decoration: none;
`;
export const Info = ({
accent = 'blue',
text,
buttonTitle,
onClick,
to,
}: InfoProps) => {
const theme = useTheme();
return (
<StyledInfo accent={accent}>
<StyledTextContainer>
<StyledIconInfoCircle size={theme.icon.size.md} />
{text}
</StyledTextContainer>
{buttonTitle && to && (
<StyledLink to={to}>
<Button
title={buttonTitle}
size={'small'}
variant={'secondary'}
accent={accent}
/>
</StyledLink>
)}
{buttonTitle && onClick && !to && (
<Button
title={buttonTitle}
onClick={onClick}
size={'small'}
variant={'secondary'}
accent={accent}
/>
)}
</StyledInfo>
);
};

View File

@ -1,43 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { CatalogDecorator, CatalogStory, ComponentDecorator } from 'twenty-ui';
import { Info, InfoAccent } from '@/ui/display/info/components/Info';
const meta: Meta<typeof Info> = {
title: 'UI/Display/Info',
component: Info,
};
export default meta;
type Story = StoryObj<typeof Info>;
export const Default: Story = {
args: {
accent: 'blue',
text: 'An info component',
buttonTitle: 'Update',
},
decorators: [ComponentDecorator],
};
export const Catalog: CatalogStory<Story, typeof Info> = {
args: {
text: 'An info component',
buttonTitle: 'Update',
},
argTypes: {
accent: { control: false },
},
parameters: {
catalog: {
dimensions: [
{
name: 'accents',
values: ['blue', 'danger'] satisfies InfoAccent[],
props: (accent: InfoAccent) => ({ accent }),
},
],
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,70 +0,0 @@
import styled from '@emotion/styled';
import { Loader, ThemeColor, themeColorSchema } from 'twenty-ui';
const StyledStatus = styled.h3<{
color: ThemeColor;
weight: 'regular' | 'medium';
isLoaderVisible: boolean;
}>`
align-items: center;
background: ${({ color, theme }) => theme.tag.background[color]};
border-radius: ${({ theme }) => theme.border.radius.pill};
color: ${({ color, theme }) => theme.tag.text[color]};
display: inline-flex;
font-size: ${({ theme }) => theme.font.size.md};
font-style: normal;
font-weight: ${({ theme, weight }) => theme.font.weight[weight]};
gap: ${({ theme }) => theme.spacing(1)};
height: ${({ theme }) => theme.spacing(5)};
margin: 0;
overflow: hidden;
padding: 0
${({ theme, isLoaderVisible }) =>
isLoaderVisible ? theme.spacing(1) : theme.spacing(2)}
0 ${({ theme }) => theme.spacing(2)};
&:before {
background-color: ${({ color, theme }) => theme.tag.text[color]};
border-radius: ${({ theme }) => theme.border.radius.rounded};
content: '';
display: block;
flex-shrink: 0;
height: ${({ theme }) => theme.spacing(1)};
width: ${({ theme }) => theme.spacing(1)};
}
`;
const StyledContent = styled.span`
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
type StatusProps = {
className?: string;
color: ThemeColor;
isLoaderVisible?: boolean;
text: string;
onClick?: () => void;
weight?: 'regular' | 'medium';
};
export const Status = ({
className,
color,
isLoaderVisible = false,
text,
onClick,
weight = 'regular',
}: StatusProps) => (
<StyledStatus
className={className}
color={themeColorSchema.catch('gray').parse(color)}
onClick={onClick}
weight={weight}
isLoaderVisible={isLoaderVisible}
>
<StyledContent>{text}</StyledContent>
{isLoaderVisible ? <Loader color={color} /> : null}
</StyledStatus>
);

View File

@ -1,68 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { expect, fn, userEvent, within } from '@storybook/test';
import {
CatalogDecorator,
CatalogStory,
ComponentDecorator,
MAIN_COLOR_NAMES,
ThemeColor,
} from 'twenty-ui';
import { Status } from '../Status';
const meta: Meta<typeof Status> = {
title: 'UI/Display/Status/Status',
component: Status,
args: {
text: 'Urgent',
weight: 'medium',
},
};
export default meta;
type Story = StoryObj<typeof Status>;
export const Default: Story = {
args: {
color: 'red',
onClick: fn(),
},
decorators: [ComponentDecorator],
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const status = canvas.getByRole('heading', { level: 3 });
await userEvent.click(status);
expect(args.onClick).toHaveBeenCalled();
},
};
export const WithLongText: Story = {
decorators: [ComponentDecorator],
args: {
color: 'green',
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
},
parameters: {
container: { width: 100 },
},
};
export const Catalog: CatalogStory<Story, typeof Status> = {
argTypes: {
color: { control: false },
},
parameters: {
catalog: {
dimensions: [
{
name: 'colors',
values: MAIN_COLOR_NAMES,
props: (color: ThemeColor) => ({ color }),
},
],
},
},
decorators: [CatalogDecorator],
};

View File

@ -1,34 +0,0 @@
import React from 'react';
import styled from '@emotion/styled';
const StyledContainer = styled.div`
display: flex;
align-items: center;
width: 100%;
font-size: ${({ theme }) => theme.font.size.md};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
color: ${({ theme }) => theme.font.color.extraLight};
&:before,
&:after {
content: '';
height: 1px;
flex-grow: 1;
background: ${({ theme }) => theme.background.transparent.light};
}
&:before {
margin: 0 ${({ theme }) => theme.spacing(4)} 0 0;
}
&:after {
margin: 0 0 0 ${({ theme }) => theme.spacing(4)};
}
`;
export const SeparatorLineText = ({
children,
}: {
children: React.ReactNode;
}) => {
return <StyledContainer>{children}</StyledContainer>;
};

View File

@ -1,16 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from 'twenty-ui';
import { SeparatorLineText } from '../SeparatorLineText';
const meta: Meta<typeof SeparatorLineText> = {
title: 'UI/Display/Text/SeparatorLineText',
component: SeparatorLineText,
args: { children: 'Or' },
decorators: [ComponentDecorator],
};
export default meta;
type Story = StoryObj<typeof SeparatorLineText>;
export const Default: Story = {};

View File

@ -1,18 +1,18 @@
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useAuth } from '@/auth/hooks/useAuth';
import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
import { availableSSOIdentityProvidersState } from '@/auth/states/availableWorkspacesForSSO';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import {
SignInUpStep,
signInUpStepState,
} from '@/auth/states/signInUpStepState';
import { tokenPairState } from '@/auth/states/tokenPairState';
import { AppPath } from '@/types/AppPath';
import { useGenerateJwtMutation } from '~/generated/graphql';
import { isDefined } from '~/utils/isDefined';
import { sleep } from '~/utils/sleep';
import { useSSO } from '@/auth/sign-in-up/hooks/useSSO';
import {
SignInUpStep,
signInUpStepState,
} from '@/auth/states/signInUpStepState';
import { availableSSOIdentityProvidersState } from '@/auth/states/availableWorkspacesForSSO';
import { useAuth } from '@/auth/hooks/useAuth';
export const useWorkspaceSwitching = () => {
const setTokenPair = useSetRecoilState(tokenPairState);