diff --git a/front/src/modules/ui/field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx b/front/src/modules/ui/field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx new file mode 100644 index 000000000..f787e2d03 --- /dev/null +++ b/front/src/modules/ui/field/meta-types/display/components/__stories__/DateFieldDisplay.stories.tsx @@ -0,0 +1,78 @@ +import { useEffect } from 'react'; +import { Meta, StoryObj } from '@storybook/react'; + +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; + +import { useDateField } from '../../../hooks/useDateField'; +import { DateFieldDisplay } from '../DateFieldDisplay'; + +import { FieldDisplayContextProvider } from './FieldDisplayContextProvider'; + +const formattedDate = new Date(); + +const DateFieldValueSetterEffect = ({ value }: { value: string }) => { + const { setFieldValue } = useDateField(); + + useEffect(() => { + setFieldValue(value); + }, [setFieldValue, value]); + + return <>; +}; + +type DateFieldDisplayWithContextProps = { + value: string; + entityId?: string; +}; + +const DateFieldDisplayWithContext = ({ + value, + entityId, +}: DateFieldDisplayWithContextProps) => { + return ( + + + + + ); +}; + +const meta: Meta = { + title: 'UI/Field/DateFieldDisplay', + component: DateFieldDisplayWithContext, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + value: formattedDate.toISOString(), + }, +}; + +export const Elipsis: Story = { + args: { + value: formattedDate.toISOString(), + }, + argTypes: { + value: { control: false }, + }, + parameters: { + container: { + width: 50, + }, + }, + decorators: [ComponentDecorator], +}; diff --git a/front/src/modules/ui/field/meta-types/display/components/__stories__/DoubleTextFieldDisplay.stories.tsx b/front/src/modules/ui/field/meta-types/display/components/__stories__/DoubleTextFieldDisplay.stories.tsx new file mode 100644 index 000000000..edf9739d5 --- /dev/null +++ b/front/src/modules/ui/field/meta-types/display/components/__stories__/DoubleTextFieldDisplay.stories.tsx @@ -0,0 +1,102 @@ +import React, { useEffect } from 'react'; +import { Meta, StoryObj } from '@storybook/react'; + +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; + +import { useDoubleTextField } from '../../../hooks/useDoubleTextField'; +import { DoubleTextFieldDisplay } from '../DoubleTextFieldDisplay'; // Import your component + +import { FieldDisplayContextProvider } from './FieldDisplayContextProvider'; + +const DoubleTextFieldDisplayValueSetterEffect = ({ + firstValue, + secondValue, +}: { + firstValue: string; + secondValue: string; +}) => { + const { setFirstValue, setSecondValue } = useDoubleTextField(); + + useEffect(() => { + setFirstValue(firstValue); + setSecondValue(secondValue); + }, [setFirstValue, setSecondValue, firstValue, secondValue]); + + return <>; +}; + +type DoubleTextFieldDisplayWithContextProps = { + firstValue: string; + secondValue: string; + entityId?: string; +}; + +const DoubleTextFieldDisplayWithContext = ({ + firstValue, + secondValue, + entityId, +}: DoubleTextFieldDisplayWithContextProps) => { + return ( + + + + + ); +}; + +const meta: Meta = { + title: 'UI/Field/DoubleTextFieldDisplay', + component: DoubleTextFieldDisplayWithContext, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + firstValue: 'Lorem', + secondValue: 'ipsum', + }, +}; + +export const CustomValues: Story = { + args: { + firstValue: 'Lorem', + secondValue: 'ipsum', + }, +}; + +export const Elipsis: Story = { + args: { + firstValue: + 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.', + secondValue: 'ipsum dolor sit amet, consectetur adipiscing elit.', + }, + argTypes: { + firstValue: { control: true }, + secondValue: { control: true }, + }, + parameters: { + container: { + width: 100, + }, + }, + decorators: [ComponentDecorator], +}; diff --git a/front/src/modules/ui/field/meta-types/display/components/__stories__/EmailFieldDisplay.stories.tsx b/front/src/modules/ui/field/meta-types/display/components/__stories__/EmailFieldDisplay.stories.tsx new file mode 100644 index 000000000..fc68abb42 --- /dev/null +++ b/front/src/modules/ui/field/meta-types/display/components/__stories__/EmailFieldDisplay.stories.tsx @@ -0,0 +1,80 @@ +import { useEffect } from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { Meta, StoryObj } from '@storybook/react'; + +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; + +import { useEmailField } from '../../../hooks/useEmailField'; +import { EmailFieldDisplay } from '../EmailFieldDisplay'; + +import { FieldDisplayContextProvider } from './FieldDisplayContextProvider'; + +const EmailFieldValueSetterEffect = ({ value }: { value: string }) => { + const { setFieldValue } = useEmailField(); + + useEffect(() => { + setFieldValue(value); + }, [setFieldValue, value]); + + return <>; +}; + +type EmailFieldDisplayWithContextProps = { + value: string; + entityId?: string; +}; + +const EmailFieldDisplayWithContext = ({ + value, + entityId, +}: EmailFieldDisplayWithContextProps) => { + return ( + + + + + + + ); +}; + +const meta: Meta = { + title: 'UI/Field/EmailFieldDisplay', + component: EmailFieldDisplayWithContext, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + value: 'Test@Test.test', + }, +}; + +export const Elipsis: Story = { + args: { + value: 'Test@Test.test', + }, + argTypes: { + value: { control: false }, + }, + parameters: { + container: { + width: 50, + }, + }, + decorators: [ComponentDecorator], +}; diff --git a/front/src/modules/ui/field/meta-types/display/components/__stories__/NumberFieldDisplay.stories.tsx b/front/src/modules/ui/field/meta-types/display/components/__stories__/NumberFieldDisplay.stories.tsx new file mode 100644 index 000000000..e333d1b34 --- /dev/null +++ b/front/src/modules/ui/field/meta-types/display/components/__stories__/NumberFieldDisplay.stories.tsx @@ -0,0 +1,109 @@ +import { useEffect } from 'react'; +import { Meta, StoryObj } from '@storybook/react'; +import { v4 } from 'uuid'; + +import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator'; +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; +import { CatalogStory } from '~/testing/types'; + +import { useNumberField } from '../../../hooks/useNumberField'; +import { NumberFieldDisplay } from '../NumberFieldDisplay'; + +import { FieldDisplayContextProvider } from './FieldDisplayContextProvider'; + +const NumberFieldValueSetterEffect = ({ value }: { value: number }) => { + const { setFieldValue } = useNumberField(); + + useEffect(() => { + setFieldValue(value); + }, [setFieldValue, value]); + + return <>; +}; + +type NumberFieldDisplayWithContextProps = { + value: number; + entityId?: string; +}; + +const NumberFieldDisplayWithContext = ({ + value, + entityId, +}: NumberFieldDisplayWithContextProps) => { + return ( + + + + + ); +}; + +const meta: Meta = { + title: 'UI/Field/NumberFieldDisplay', + component: NumberFieldDisplayWithContext, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + value: 100, + }, +}; + +export const Elipsis: Story = { + args: { + value: 1e100, + }, + argTypes: { + value: { control: false }, + }, + parameters: { + container: { + width: 100, + }, + }, + decorators: [ComponentDecorator], +}; + +export const Catalog: CatalogStory< + Story, + typeof NumberFieldDisplayWithContext +> = { + argTypes: { + value: { control: false }, + }, + parameters: { + catalog: { + dimensions: [ + { + name: 'value', + values: [ + 100, 1000, -1000, 1e10, 1.357802, -1.283, 0, + ] satisfies number[], + props: (value: number) => ({ value, entityId: v4() }), + }, + ], + options: { + elementContainer: { + width: 100, + }, + }, + }, + }, + decorators: [CatalogDecorator], +}; diff --git a/front/src/modules/ui/field/meta-types/display/components/__stories__/PhoneFieldDisplay.stories.tsx b/front/src/modules/ui/field/meta-types/display/components/__stories__/PhoneFieldDisplay.stories.tsx new file mode 100644 index 000000000..d202745bf --- /dev/null +++ b/front/src/modules/ui/field/meta-types/display/components/__stories__/PhoneFieldDisplay.stories.tsx @@ -0,0 +1,80 @@ +import { useEffect } from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { Meta, StoryObj } from '@storybook/react'; + +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; + +import { usePhoneField } from '../../../hooks/usePhoneField'; +import { PhoneFieldDisplay } from '../PhoneFieldDisplay'; + +import { FieldDisplayContextProvider } from './FieldDisplayContextProvider'; + +const PhoneFieldValueSetterEffect = ({ value }: { value: string }) => { + const { setFieldValue } = usePhoneField(); + + useEffect(() => { + setFieldValue(value); + }, [setFieldValue, value]); + + return <>; +}; + +type PhoneFieldDisplayWithContextProps = { + value: string; + entityId?: string; +}; + +const PhoneFieldDisplayWithContext = ({ + value, + entityId, +}: PhoneFieldDisplayWithContextProps) => { + return ( + + + + + + + ); +}; + +const meta: Meta = { + title: 'UI/Field/PhoneFieldDisplay', + component: PhoneFieldDisplayWithContext, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + value: '362763872687362', + }, +}; + +export const Elipsis: Story = { + args: { + value: '362763872687362', + }, + argTypes: { + value: { control: false }, + }, + parameters: { + container: { + width: 50, + }, + }, + decorators: [ComponentDecorator], +}; diff --git a/front/src/modules/ui/field/meta-types/display/components/__stories__/URLFieldDisplay.stories.tsx b/front/src/modules/ui/field/meta-types/display/components/__stories__/URLFieldDisplay.stories.tsx new file mode 100644 index 000000000..4d50e98d1 --- /dev/null +++ b/front/src/modules/ui/field/meta-types/display/components/__stories__/URLFieldDisplay.stories.tsx @@ -0,0 +1,80 @@ +import { useEffect } from 'react'; +import { MemoryRouter } from 'react-router'; +import { Meta, StoryObj } from '@storybook/react'; + +import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator'; + +import { useURLField } from '../../../hooks/useURLField'; +import { URLFieldDisplay } from '../URLFieldDisplay'; + +import { FieldDisplayContextProvider } from './FieldDisplayContextProvider'; + +const URLFieldValueSetterEffect = ({ value }: { value: string }) => { + const { setFieldValue } = useURLField(); + + useEffect(() => { + setFieldValue(value); + }, [setFieldValue, value]); + + return <>; +}; + +type URLFieldDisplayWithContextProps = { + value: string; + entityId?: string; +}; + +const URLFieldDisplayWithContext = ({ + value, + entityId, +}: URLFieldDisplayWithContextProps) => { + return ( + + + + + + + ); +}; + +const meta: Meta = { + title: 'UI/Field/URLFieldDisplay', + component: URLFieldDisplayWithContext, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + value: 'https://github.com/GitStartHQ', + }, +}; + +export const Elipsis: Story = { + args: { + value: 'https://www.instagram.com/gitstart/', + }, + argTypes: { + value: { control: true }, + }, + parameters: { + container: { + width: 200, + }, + }, + decorators: [ComponentDecorator], +}; diff --git a/front/src/modules/ui/field/meta-types/display/content-display/components/DateDisplay.tsx b/front/src/modules/ui/field/meta-types/display/content-display/components/DateDisplay.tsx index 054c0213b..4e41fb3bc 100644 --- a/front/src/modules/ui/field/meta-types/display/content-display/components/DateDisplay.tsx +++ b/front/src/modules/ui/field/meta-types/display/content-display/components/DateDisplay.tsx @@ -1,9 +1,11 @@ import { formatToHumanReadableDate } from '~/utils'; +import { EllipsisDisplay } from './EllipsisDisplay'; + type DateDisplayProps = { value: Date | string | null | undefined; }; export const DateDisplay = ({ value }: DateDisplayProps) => ( -
{value && formatToHumanReadableDate(value)}
+ {value && formatToHumanReadableDate(value)} ); diff --git a/front/src/modules/ui/field/meta-types/display/content-display/components/EllipsisDisplay.tsx b/front/src/modules/ui/field/meta-types/display/content-display/components/EllipsisDisplay.tsx new file mode 100644 index 000000000..998f5ab16 --- /dev/null +++ b/front/src/modules/ui/field/meta-types/display/content-display/components/EllipsisDisplay.tsx @@ -0,0 +1,10 @@ +import styled from '@emotion/styled'; + +const StyledEllipsisDisplay = styled.div` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 100%; +`; + +export { StyledEllipsisDisplay as EllipsisDisplay }; diff --git a/front/src/modules/ui/field/meta-types/display/content-display/components/EmailDisplay.tsx b/front/src/modules/ui/field/meta-types/display/content-display/components/EmailDisplay.tsx index 34f8a30e7..a191478ab 100644 --- a/front/src/modules/ui/field/meta-types/display/content-display/components/EmailDisplay.tsx +++ b/front/src/modules/ui/field/meta-types/display/content-display/components/EmailDisplay.tsx @@ -2,6 +2,8 @@ import { MouseEvent } from 'react'; import { ContactLink } from '@/ui/link/components/ContactLink'; +import { EllipsisDisplay } from './EllipsisDisplay'; + const validateEmail = (email: string) => { const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailPattern.test(email.trim()); @@ -11,16 +13,19 @@ type EmailDisplayProps = { value: string | null; }; -export const EmailDisplay = ({ value }: EmailDisplayProps) => - value && validateEmail(value) ? ( - ) => { - event.stopPropagation(); - }} - > - {value} - - ) : ( - {value} - ); +export const EmailDisplay = ({ value }: EmailDisplayProps) => ( + + {value && validateEmail(value) ? ( + ) => { + event.stopPropagation(); + }} + > + {value} + + ) : ( + {value} + )} + +); diff --git a/front/src/modules/ui/field/meta-types/display/content-display/components/MoneyDisplay.tsx b/front/src/modules/ui/field/meta-types/display/content-display/components/MoneyDisplay.tsx index d39e10092..33fc3ba73 100644 --- a/front/src/modules/ui/field/meta-types/display/content-display/components/MoneyDisplay.tsx +++ b/front/src/modules/ui/field/meta-types/display/content-display/components/MoneyDisplay.tsx @@ -1,20 +1,11 @@ -import styled from '@emotion/styled'; - import { formatNumber } from '~/utils/format/number'; -const StyledTextInputDisplay = styled.div` - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 100%; -`; +import { EllipsisDisplay } from './EllipsisDisplay'; type MoneyDisplayProps = { value: number | null; }; export const MoneyDisplay = ({ value }: MoneyDisplayProps) => ( - - {value ? `$${formatNumber(value)}` : ''} - + {value ? `$${formatNumber(value)}` : ''} ); diff --git a/front/src/modules/ui/field/meta-types/display/content-display/components/NumberDisplay.tsx b/front/src/modules/ui/field/meta-types/display/content-display/components/NumberDisplay.tsx index bca6d61b7..5a8dc13c3 100644 --- a/front/src/modules/ui/field/meta-types/display/content-display/components/NumberDisplay.tsx +++ b/front/src/modules/ui/field/meta-types/display/content-display/components/NumberDisplay.tsx @@ -1,20 +1,11 @@ -import styled from '@emotion/styled'; - import { formatNumber } from '~/utils/format/number'; -const StyledNumberDisplay = styled.div` - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 100%; -`; +import { EllipsisDisplay } from './EllipsisDisplay'; type NumberDisplayProps = { value: string | number | null; }; export const NumberDisplay = ({ value }: NumberDisplayProps) => ( - - {value && formatNumber(Number(value))} - + {value && formatNumber(Number(value))} ); diff --git a/front/src/modules/ui/field/meta-types/display/content-display/components/PhoneDisplay.tsx b/front/src/modules/ui/field/meta-types/display/content-display/components/PhoneDisplay.tsx index f593ff2c2..933b173a6 100644 --- a/front/src/modules/ui/field/meta-types/display/content-display/components/PhoneDisplay.tsx +++ b/front/src/modules/ui/field/meta-types/display/content-display/components/PhoneDisplay.tsx @@ -3,20 +3,25 @@ import { isValidPhoneNumber, parsePhoneNumber } from 'libphonenumber-js'; import { ContactLink } from '@/ui/link/components/ContactLink'; +import { EllipsisDisplay } from './EllipsisDisplay'; + type PhoneDisplayProps = { value: string | null; }; -export const PhoneDisplay = ({ value }: PhoneDisplayProps) => - value && isValidPhoneNumber(value) ? ( - ) => { - event.stopPropagation(); - }} - > - {parsePhoneNumber(value, 'FR')?.formatInternational() || value} - - ) : ( - {value} - ); +export const PhoneDisplay = ({ value }: PhoneDisplayProps) => ( + + {value && isValidPhoneNumber(value) ? ( + ) => { + event.stopPropagation(); + }} + > + {parsePhoneNumber(value, 'FR')?.formatInternational() || value} + + ) : ( + {value} + )} + +); diff --git a/front/src/modules/ui/field/meta-types/display/content-display/components/TextDisplay.tsx b/front/src/modules/ui/field/meta-types/display/content-display/components/TextDisplay.tsx index 9229b84aa..fea7d6fdd 100644 --- a/front/src/modules/ui/field/meta-types/display/content-display/components/TextDisplay.tsx +++ b/front/src/modules/ui/field/meta-types/display/content-display/components/TextDisplay.tsx @@ -1,16 +1,9 @@ -import styled from '@emotion/styled'; - -const StyledTextInputDisplay = styled.div` - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 100%; -`; +import { EllipsisDisplay } from './EllipsisDisplay'; type TextDisplayProps = { text: string; }; export const TextDisplay = ({ text }: TextDisplayProps) => ( - {text} + {text} ); diff --git a/front/src/modules/ui/field/meta-types/display/content-display/components/URLDisplay.tsx b/front/src/modules/ui/field/meta-types/display/content-display/components/URLDisplay.tsx index cf9937882..7739c6f93 100644 --- a/front/src/modules/ui/field/meta-types/display/content-display/components/URLDisplay.tsx +++ b/front/src/modules/ui/field/meta-types/display/content-display/components/URLDisplay.tsx @@ -4,6 +4,8 @@ import styled from '@emotion/styled'; import { RoundedLink } from '@/ui/link/components/RoundedLink'; import { LinkType, SocialLink } from '@/ui/link/components/SocialLink'; +import { EllipsisDisplay } from './EllipsisDisplay'; + const StyledRawLink = styled(RoundedLink)` overflow: hidden; @@ -50,14 +52,18 @@ export const URLDisplay = ({ value }: URLDisplayProps) => { if (type === LinkType.LinkedIn || type === LinkType.Twitter) { return ( - - {displayedValue} - + + + {displayedValue} + + ); } return ( - - {displayedValue} - + + + {displayedValue} + + ); }; diff --git a/front/src/modules/ui/link/components/ContactLink.tsx b/front/src/modules/ui/link/components/ContactLink.tsx index e09a0c212..4afea7333 100644 --- a/front/src/modules/ui/link/components/ContactLink.tsx +++ b/front/src/modules/ui/link/components/ContactLink.tsx @@ -16,8 +16,10 @@ const StyledClickable = styled.div` a { color: inherit; + overflow: hidden; text-decoration: underline; text-decoration-color: ${({ theme }) => theme.border.color.strong}; + text-overflow: ellipsis; &:hover { text-decoration-color: ${({ theme }) => theme.font.color.primary}; diff --git a/front/src/modules/ui/link/components/PrimaryLink.tsx b/front/src/modules/ui/link/components/PrimaryLink.tsx index 27cac16e4..94306d8db 100644 --- a/front/src/modules/ui/link/components/PrimaryLink.tsx +++ b/front/src/modules/ui/link/components/PrimaryLink.tsx @@ -14,7 +14,9 @@ const StyledClickable = styled.div` a { color: ${({ theme }) => theme.font.color.tertiary}; font-size: ${({ theme }) => theme.font.size.sm}; + overflow: hidden; text-decoration: underline; + text-overflow: ellipsis; } `; diff --git a/front/src/modules/ui/link/components/RawLink.tsx b/front/src/modules/ui/link/components/RawLink.tsx index a02aad799..cb1444417 100644 --- a/front/src/modules/ui/link/components/RawLink.tsx +++ b/front/src/modules/ui/link/components/RawLink.tsx @@ -16,6 +16,8 @@ const StyledClickable = styled.div` a { color: inherit; + overflow: hidden; + text-overflow: ellipsis; } `; diff --git a/front/src/modules/ui/link/components/RoundedLink.tsx b/front/src/modules/ui/link/components/RoundedLink.tsx index f25905f3f..dae52c6de 100644 --- a/front/src/modules/ui/link/components/RoundedLink.tsx +++ b/front/src/modules/ui/link/components/RoundedLink.tsx @@ -17,7 +17,9 @@ const StyledClickable = styled.div` a { color: inherit; + overflow: hidden; text-decoration: none; + text-overflow: ellipsis; } `; diff --git a/front/src/testing/ComponentStorybookLayout.tsx b/front/src/testing/ComponentStorybookLayout.tsx index 91b6b390d..57fbe6554 100644 --- a/front/src/testing/ComponentStorybookLayout.tsx +++ b/front/src/testing/ComponentStorybookLayout.tsx @@ -10,7 +10,7 @@ const StyledLayout = styled.div<{ width?: number }>` height: fit-content; max-width: calc(100% - 40px); - min-width: 300px; + min-width: ${({ width }) => (width ? 'unset' : '300px')}; padding: 20px; width: ${({ width }) => (width ? width + 'px' : 'fit-content')}; `; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..fb57ccd13 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +