Migrate to twenty-ui - navigation/link (#7837)
This PR was created by [GitStart](https://gitstart.com/) to address the requirements from this ticket: [TWNTY-7535](https://clients.gitstart.com/twenty/5449/tickets/TWNTY-7535). --- ### Description. Migrate link components to `twenty-ui` \ \ Fixes #7535 --------- Co-authored-by: gitstart-twenty <gitstart-twenty@users.noreply.github.com> Co-authored-by: gitstart-twenty <140154534+gitstart-twenty@users.noreply.github.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
committed by
GitHub
parent
02c34d547f
commit
430644448a
@ -1,8 +1,8 @@
|
||||
import { MouseEvent } from 'react';
|
||||
|
||||
import { ContactLink } from '@/ui/navigation/link/components/ContactLink';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { ContactLink } from 'twenty-ui';
|
||||
import { EllipsisDisplay } from './EllipsisDisplay';
|
||||
|
||||
const validateEmail = (email: string) => {
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { useMemo } from 'react';
|
||||
import { THEME_COMMON } from 'twenty-ui';
|
||||
import { RoundedLink, THEME_COMMON } from 'twenty-ui';
|
||||
|
||||
import { FieldEmailsValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
import { RoundedLink } from '@/ui/navigation/link/components/RoundedLink';
|
||||
import styled from '@emotion/styled';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
import { RoundedLink } from '@/ui/navigation/link/components/RoundedLink';
|
||||
import {
|
||||
LinkType,
|
||||
SocialLink,
|
||||
} from '@/ui/navigation/link/components/SocialLink';
|
||||
import { LinkType, RoundedLink, SocialLink } from 'twenty-ui';
|
||||
|
||||
type LinkDisplayProps = {
|
||||
value?: { url: string; label?: string };
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
import { useMemo } from 'react';
|
||||
import { styled } from '@linaria/react';
|
||||
import { THEME_COMMON } from 'twenty-ui';
|
||||
import { useMemo } from 'react';
|
||||
import { LinkType, RoundedLink, SocialLink, THEME_COMMON } from 'twenty-ui';
|
||||
|
||||
import { FieldLinksValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
import { RoundedLink } from '@/ui/navigation/link/components/RoundedLink';
|
||||
import {
|
||||
LinkType,
|
||||
SocialLink,
|
||||
} from '@/ui/navigation/link/components/SocialLink';
|
||||
import { checkUrlType } from '~/utils/checkUrlType';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { getAbsoluteUrl } from '~/utils/url/getAbsoluteUrl';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { parsePhoneNumber, PhoneNumber } from 'libphonenumber-js';
|
||||
import { MouseEvent } from 'react';
|
||||
import { ContactLink } from 'twenty-ui';
|
||||
|
||||
import { ContactLink } from '@/ui/navigation/link/components/ContactLink';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type PhoneDisplayProps = {
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useMemo } from 'react';
|
||||
import { THEME_COMMON } from 'twenty-ui';
|
||||
import { RoundedLink, THEME_COMMON } from 'twenty-ui';
|
||||
|
||||
import { FieldPhonesValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ExpandableList } from '@/ui/layout/expandable-list/components/ExpandableList';
|
||||
import { RoundedLink } from '@/ui/navigation/link/components/RoundedLink';
|
||||
|
||||
import { parsePhoneNumber } from 'libphonenumber-js';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
import { MouseEvent } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { MouseEvent } from 'react';
|
||||
|
||||
import { RoundedLink } from '@/ui/navigation/link/components/RoundedLink';
|
||||
import {
|
||||
LinkType,
|
||||
SocialLink,
|
||||
} from '@/ui/navigation/link/components/SocialLink';
|
||||
import { checkUrlType } from '~/utils/checkUrlType';
|
||||
|
||||
import { LinkType, RoundedLink, SocialLink } from 'twenty-ui';
|
||||
import { EllipsisDisplay } from './EllipsisDisplay';
|
||||
|
||||
const StyledRawLink = styled(RoundedLink)`
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import React, { MouseEvent, useMemo, useRef, useState } from 'react';
|
||||
import { MouseEvent, useMemo, useRef, useState } from 'react';
|
||||
import { IconChevronDown, IconComponent } from 'twenty-ui';
|
||||
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
@ -10,8 +10,8 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
|
||||
import { SelectHotkeyScope } from '../types/SelectHotkeyScope';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { SelectHotkeyScope } from '../types/SelectHotkeyScope';
|
||||
|
||||
export type SelectOption<Value extends string | number | null> = {
|
||||
value: Value;
|
||||
|
||||
@ -1,88 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
import { VisibilityHiddenInput } from 'twenty-ui';
|
||||
|
||||
export type ToggleSize = 'small' | 'medium';
|
||||
|
||||
type ContainerProps = {
|
||||
isOn: boolean;
|
||||
color?: string;
|
||||
toggleSize: ToggleSize;
|
||||
'data-disabled'?: boolean;
|
||||
};
|
||||
|
||||
const StyledContainer = styled.label<ContainerProps>`
|
||||
align-items: center;
|
||||
background-color: ${({ theme, isOn, color }) =>
|
||||
isOn ? (color ?? theme.color.blue) : theme.background.transparent.medium};
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: ${({ toggleSize }) => (toggleSize === 'small' ? 16 : 20)}px;
|
||||
transition: background-color 0.3s ease;
|
||||
width: ${({ toggleSize }) => (toggleSize === 'small' ? 24 : 32)}px;
|
||||
opacity: ${({ 'data-disabled': disabled }) => (disabled ? 0.5 : 1)};
|
||||
pointer-events: ${({ 'data-disabled': disabled }) =>
|
||||
disabled ? 'none' : 'auto'};
|
||||
`;
|
||||
|
||||
const StyledCircle = styled(motion.span)<{
|
||||
size: ToggleSize;
|
||||
}>`
|
||||
display: block;
|
||||
background-color: ${({ theme }) => theme.background.primary};
|
||||
border-radius: 50%;
|
||||
height: ${({ size }) => (size === 'small' ? 12 : 16)}px;
|
||||
width: ${({ size }) => (size === 'small' ? 12 : 16)}px;
|
||||
`;
|
||||
|
||||
export type ToggleProps = {
|
||||
id?: string;
|
||||
value?: boolean;
|
||||
onChange?: (value: boolean, e?: React.MouseEvent<HTMLDivElement>) => void;
|
||||
color?: string;
|
||||
toggleSize?: ToggleSize;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const Toggle = ({
|
||||
id,
|
||||
value = false,
|
||||
onChange,
|
||||
color,
|
||||
toggleSize = 'medium',
|
||||
className,
|
||||
disabled,
|
||||
}: ToggleProps) => {
|
||||
const circleVariants = {
|
||||
on: { x: toggleSize === 'small' ? 10 : 14 },
|
||||
off: { x: 2 },
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledContainer
|
||||
isOn={value}
|
||||
color={color}
|
||||
toggleSize={toggleSize}
|
||||
className={className}
|
||||
data-disabled={disabled}
|
||||
>
|
||||
<VisibilityHiddenInput
|
||||
id={id}
|
||||
type="checkbox"
|
||||
checked={value}
|
||||
disabled={disabled}
|
||||
onChange={(event) => {
|
||||
onChange?.(event.target.checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
<StyledCircle
|
||||
animate={value ? 'on' : 'off'}
|
||||
variants={circleVariants}
|
||||
size={toggleSize}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -1,10 +1,9 @@
|
||||
import { useState } from 'react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/test';
|
||||
import { ComponentDecorator } from 'twenty-ui';
|
||||
import { useState } from 'react';
|
||||
import { ComponentDecorator, IconPlus } from 'twenty-ui';
|
||||
|
||||
import { Select, SelectProps } from '../Select';
|
||||
import { IconPlus } from 'packages/twenty-ui';
|
||||
|
||||
type RenderProps = SelectProps<string | number | null>;
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import { IconExternalLink } from 'twenty-ui';
|
||||
import { IconExternalLink, UndecoratedLink } from 'twenty-ui';
|
||||
|
||||
export const RightDrawerTopBarExpandButton = ({ to }: { to: string }) => {
|
||||
const { closeRightDrawer } = useRightDrawer();
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledButtonLink = styled.a`
|
||||
align-items: center;
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
display: flex;
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
padding: 0 ${({ theme }) => theme.spacing(1)};
|
||||
text-decoration: none;
|
||||
|
||||
:hover {
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ActionLink = (props: React.ComponentProps<'a'>) => {
|
||||
return (
|
||||
<StyledButtonLink
|
||||
href={props.href}
|
||||
onClick={props.onClick}
|
||||
target={props.target}
|
||||
rel={props.rel}
|
||||
>
|
||||
{props.children}
|
||||
</StyledButtonLink>
|
||||
);
|
||||
};
|
||||
@ -1,68 +0,0 @@
|
||||
import { Toggle } from '@/ui/input/components/Toggle';
|
||||
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
|
||||
import styled from '@emotion/styled';
|
||||
import { useId } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { IconTool, MAIN_COLORS } from 'twenty-ui';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledLabel = styled.label`
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
font-size: ${({ theme }) => theme.font.size.sm};
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledIconContainer = styled.div`
|
||||
border-right: 1px solid ${MAIN_COLORS.yellow};
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
left: ${({ theme }) => theme.spacing(-5)};
|
||||
`;
|
||||
|
||||
const StyledToggleContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledIconTool = styled(IconTool)`
|
||||
margin-right: ${({ theme }) => theme.spacing(0.5)};
|
||||
`;
|
||||
|
||||
export const AdvancedSettingsToggle = () => {
|
||||
const [isAdvancedModeEnabled, setIsAdvancedModeEnabled] = useRecoilState(
|
||||
isAdvancedModeEnabledState,
|
||||
);
|
||||
const inputId = useId();
|
||||
|
||||
const onChange = (newValue: boolean) => {
|
||||
setIsAdvancedModeEnabled(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledIconContainer>
|
||||
<StyledIconTool size={12} color={MAIN_COLORS.yellow} />
|
||||
</StyledIconContainer>
|
||||
<StyledToggleContainer>
|
||||
<StyledLabel htmlFor={inputId}>Advanced:</StyledLabel>
|
||||
|
||||
<Toggle
|
||||
id={inputId}
|
||||
onChange={onChange}
|
||||
color={MAIN_COLORS.yellow}
|
||||
value={isAdvancedModeEnabled}
|
||||
/>
|
||||
</StyledToggleContainer>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -1,47 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { Theme, withTheme } from '@emotion/react';
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
const StyledClickableLink = withTheme(styled.a<{
|
||||
theme: Theme;
|
||||
maxWidth?: number;
|
||||
}>`
|
||||
color: inherit;
|
||||
overflow: hidden;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: ${({ theme }) => theme.border.color.strong};
|
||||
text-overflow: ellipsis;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
|
||||
max-width: ${({ maxWidth }) => maxWidth ?? '100%'};
|
||||
|
||||
&:hover {
|
||||
text-decoration-color: ${({ theme }) => theme.font.color.primary};
|
||||
}
|
||||
`);
|
||||
|
||||
type ContactLinkProps = {
|
||||
href: string;
|
||||
children?: React.ReactNode;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
maxWidth?: number;
|
||||
};
|
||||
|
||||
export const ContactLink = ({
|
||||
href,
|
||||
children,
|
||||
onClick,
|
||||
maxWidth,
|
||||
}: ContactLinkProps) => (
|
||||
<StyledClickableLink
|
||||
maxWidth={maxWidth}
|
||||
target="_blank"
|
||||
onClick={onClick}
|
||||
href={href}
|
||||
>
|
||||
{children}
|
||||
</StyledClickableLink>
|
||||
);
|
||||
@ -1,18 +0,0 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { IconBrandGithub } from 'twenty-ui';
|
||||
|
||||
import { ActionLink } from '@/ui/navigation/link/components/ActionLink';
|
||||
|
||||
import packageJson from '../../../../../../package.json';
|
||||
import { GITHUB_LINK } from '../constants/GithubLink';
|
||||
|
||||
export const GithubVersionLink = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<ActionLink href={GITHUB_LINK} target="_blank" rel="noreferrer">
|
||||
<IconBrandGithub size={theme.icon.size.md} />
|
||||
{packageJson.version}
|
||||
</ActionLink>
|
||||
);
|
||||
};
|
||||
@ -1,37 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { Link as ReactLink } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
type RawLinkProps = {
|
||||
className?: string;
|
||||
href: string;
|
||||
children?: React.ReactNode;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
};
|
||||
|
||||
const StyledClickable = styled.div`
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
`;
|
||||
|
||||
export const RawLink = ({
|
||||
className,
|
||||
href,
|
||||
children,
|
||||
onClick,
|
||||
}: RawLinkProps) => (
|
||||
<div>
|
||||
<StyledClickable className={className}>
|
||||
<ReactLink target="_blank" onClick={onClick} to={href}>
|
||||
{children}
|
||||
</ReactLink>
|
||||
</StyledClickable>
|
||||
</div>
|
||||
);
|
||||
@ -1,96 +0,0 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { MouseEvent, useContext } from 'react';
|
||||
import { FONT_COMMON, THEME_COMMON, ThemeContext } from 'twenty-ui';
|
||||
|
||||
type RoundedLinkProps = {
|
||||
href: string;
|
||||
label?: string;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
};
|
||||
|
||||
const fontSizeMd = FONT_COMMON.size.md;
|
||||
const spacing1 = THEME_COMMON.spacing(1);
|
||||
const spacing2 = THEME_COMMON.spacing(2);
|
||||
|
||||
const spacingMultiplicator = THEME_COMMON.spacingMultiplicator;
|
||||
|
||||
const StyledLink = styled.a<{
|
||||
color: string;
|
||||
background: string;
|
||||
backgroundHover: string;
|
||||
backgroundActive: string;
|
||||
border: string;
|
||||
}>`
|
||||
align-items: center;
|
||||
background-color: ${({ background }) => background};
|
||||
border: 1px solid ${({ border }) => border};
|
||||
|
||||
border-radius: 50px;
|
||||
color: ${({ color }) => color};
|
||||
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
font-weight: ${fontSizeMd};
|
||||
|
||||
gap: ${spacing1};
|
||||
|
||||
height: 10px;
|
||||
justify-content: center;
|
||||
|
||||
max-width: calc(100% - ${spacingMultiplicator} * 2px);
|
||||
|
||||
min-width: fit-content;
|
||||
|
||||
overflow: hidden;
|
||||
padding: ${spacing1} ${spacing2};
|
||||
|
||||
text-decoration: none;
|
||||
text-overflow: ellipsis;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background-color: ${({ backgroundHover }) => backgroundHover};
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: ${({ backgroundActive }) => backgroundActive};
|
||||
}
|
||||
`;
|
||||
|
||||
export const RoundedLink = ({ label, href, onClick }: RoundedLinkProps) => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
|
||||
const background = theme.background.transparent.lighter;
|
||||
const backgroundHover = theme.background.transparent.light;
|
||||
const backgroundActive = theme.background.transparent.medium;
|
||||
const border = theme.border.color.strong;
|
||||
const color = theme.font.color.primary;
|
||||
|
||||
if (!isNonEmptyString(label)) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const handleClick = (event: MouseEvent<HTMLElement>) => {
|
||||
event.stopPropagation();
|
||||
|
||||
onClick?.(event);
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledLink
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={handleClick}
|
||||
color={color}
|
||||
background={background}
|
||||
backgroundHover={backgroundHover}
|
||||
backgroundActive={backgroundActive}
|
||||
border={border}
|
||||
>
|
||||
{label}
|
||||
</StyledLink>
|
||||
);
|
||||
};
|
||||
@ -1,25 +0,0 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { getDisplayValueByUrlType } from '~/utils/getDisplayValueByUrlType';
|
||||
|
||||
import { RoundedLink } from './RoundedLink';
|
||||
|
||||
export enum LinkType {
|
||||
Url = 'url',
|
||||
LinkedIn = 'linkedin',
|
||||
Twitter = 'twitter',
|
||||
}
|
||||
|
||||
type SocialLinkProps = {
|
||||
label: string;
|
||||
href: string;
|
||||
type: LinkType;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
};
|
||||
|
||||
export const SocialLink = ({ label, href, onClick, type }: SocialLinkProps) => {
|
||||
const displayValue =
|
||||
getDisplayValueByUrlType({ type: type, href: href }) ?? label;
|
||||
|
||||
return <RoundedLink href={href} onClick={onClick} label={displayValue} />;
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import React from 'react';
|
||||
import { Link, LinkProps } from 'react-router-dom';
|
||||
|
||||
type StyledLinkProps = LinkProps & {
|
||||
fullWidth?: boolean;
|
||||
};
|
||||
|
||||
const StyledUndecoratedLink = styled(
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
({ fullWidth: _, ...props }: StyledLinkProps) => <Link {...props} />,
|
||||
)<StyledLinkProps>`
|
||||
text-decoration: none;
|
||||
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
|
||||
`;
|
||||
|
||||
type UndecoratedLinkProps = {
|
||||
to: string | number;
|
||||
children: React.ReactNode;
|
||||
replace?: boolean;
|
||||
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
||||
fullWidth?: boolean;
|
||||
};
|
||||
|
||||
export const UndecoratedLink = ({
|
||||
children,
|
||||
to,
|
||||
replace = false,
|
||||
onClick,
|
||||
fullWidth = false,
|
||||
}: UndecoratedLinkProps) => {
|
||||
return (
|
||||
<StyledUndecoratedLink
|
||||
to={to as string}
|
||||
replace={replace}
|
||||
onClick={onClick}
|
||||
fullWidth={fullWidth}
|
||||
>
|
||||
{children}
|
||||
</StyledUndecoratedLink>
|
||||
);
|
||||
};
|
||||
@ -1,27 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { ComponentDecorator } from 'twenty-ui';
|
||||
|
||||
import { ActionLink } from '@/ui/navigation/link/components/ActionLink';
|
||||
|
||||
const meta: Meta<typeof ActionLink> = {
|
||||
title: 'UI/navigation/link/ActionLink',
|
||||
component: ActionLink,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ActionLink>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: 'Need to reset your password?',
|
||||
onClick: () => alert('Action link clicked'),
|
||||
target: undefined,
|
||||
rel: undefined,
|
||||
},
|
||||
argTypes: {
|
||||
href: { control: false },
|
||||
target: { type: 'string' },
|
||||
rel: { type: 'string' },
|
||||
},
|
||||
decorators: [ComponentDecorator],
|
||||
};
|
||||
@ -1,35 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { fn } from '@storybook/test';
|
||||
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
|
||||
import { ContactLink } from '../ContactLink';
|
||||
|
||||
const meta: Meta<typeof ContactLink> = {
|
||||
title: 'UI/Navigation/Link/ContactLink',
|
||||
component: ContactLink,
|
||||
decorators: [ComponentWithRouterDecorator],
|
||||
args: {
|
||||
href: '/test',
|
||||
children: 'Contact Link',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ContactLink>;
|
||||
const clickJestFn = fn();
|
||||
|
||||
export const Email: Story = {
|
||||
args: {
|
||||
href: `mailto:${'email@example.com'}`,
|
||||
children: 'email@example.com',
|
||||
onClick: clickJestFn,
|
||||
},
|
||||
};
|
||||
|
||||
export const Phone: Story = {
|
||||
args: {
|
||||
children: '11111111111',
|
||||
onClick: clickJestFn,
|
||||
},
|
||||
};
|
||||
@ -1,16 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
|
||||
import { GithubVersionLink } from '../GithubVersionLink';
|
||||
|
||||
const meta: Meta<typeof GithubVersionLink> = {
|
||||
title: 'UI/Navigation/Link/GithubVersionLink',
|
||||
component: GithubVersionLink,
|
||||
decorators: [ComponentWithRouterDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof GithubVersionLink>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@ -1,36 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
|
||||
import { RawLink } from '../RawLink';
|
||||
|
||||
const meta: Meta<typeof RawLink> = {
|
||||
title: 'UI/Navigation/Link/RawLink',
|
||||
component: RawLink,
|
||||
decorators: [ComponentWithRouterDecorator],
|
||||
args: {
|
||||
className: 'RawLink',
|
||||
href: '/test',
|
||||
children: 'Raw Link',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof RawLink>;
|
||||
const clickJestFn = fn();
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
onClick: clickJestFn,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await expect(clickJestFn).toHaveBeenCalledTimes(0);
|
||||
const link = canvas.getByRole('link');
|
||||
await userEvent.click(link);
|
||||
|
||||
await expect(clickJestFn).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
};
|
||||
@ -1,35 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
|
||||
import { RoundedLink } from '../RoundedLink';
|
||||
|
||||
const meta: Meta<typeof RoundedLink> = {
|
||||
title: 'UI/Navigation/Link/RoundedLink',
|
||||
component: RoundedLink,
|
||||
decorators: [ComponentWithRouterDecorator],
|
||||
args: {
|
||||
href: '/test',
|
||||
label: 'Rounded chip',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof RoundedLink>;
|
||||
const clickJestFn = fn();
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
onClick: clickJestFn,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await expect(clickJestFn).toHaveBeenCalledTimes(0);
|
||||
const link = canvas.getByRole('link');
|
||||
await userEvent.click(link);
|
||||
|
||||
await expect(clickJestFn).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
};
|
||||
@ -1,50 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { expect, fn, userEvent, within } from '@storybook/test';
|
||||
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
|
||||
import { LinkType, SocialLink } from '../SocialLink';
|
||||
|
||||
const meta: Meta<typeof SocialLink> = {
|
||||
title: 'UI/Navigation/Link/SocialLink',
|
||||
component: SocialLink,
|
||||
decorators: [ComponentWithRouterDecorator],
|
||||
args: {
|
||||
href: '/test',
|
||||
label: 'Social Link',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof SocialLink>;
|
||||
const clickJestFn = fn();
|
||||
|
||||
const linkedin: LinkType = LinkType.LinkedIn;
|
||||
const twitter: LinkType = LinkType.Twitter;
|
||||
|
||||
export const LinkedIn: Story = {
|
||||
args: {
|
||||
href: '/LinkedIn',
|
||||
label: 'LinkedIn',
|
||||
onClick: clickJestFn,
|
||||
type: linkedin,
|
||||
},
|
||||
};
|
||||
|
||||
export const Twitter: Story = {
|
||||
args: {
|
||||
href: '/Twitter',
|
||||
label: 'Twitter',
|
||||
onClick: clickJestFn,
|
||||
type: twitter,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
await expect(clickJestFn).toHaveBeenCalledTimes(0);
|
||||
const link = canvas.getByRole('link');
|
||||
await userEvent.click(link);
|
||||
|
||||
await expect(clickJestFn).toHaveBeenCalledTimes(1);
|
||||
},
|
||||
};
|
||||
@ -1,32 +0,0 @@
|
||||
import { expect } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/test';
|
||||
|
||||
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
|
||||
const meta: Meta<typeof UndecoratedLink> = {
|
||||
title: 'UI/navigation/link/UndecoratedLink',
|
||||
component: UndecoratedLink,
|
||||
decorators: [ComponentWithRouterDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof UndecoratedLink>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: 'Go Home',
|
||||
to: '/home',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const link = canvas.getByText('Go Home');
|
||||
|
||||
await userEvent.click(link);
|
||||
|
||||
const href = link.getAttribute('href');
|
||||
expect(href).toBe('/home');
|
||||
},
|
||||
};
|
||||
@ -1 +0,0 @@
|
||||
export const CAL_LINK = 'https://cal.com/team/twenty/talk-to-us';
|
||||
@ -1 +0,0 @@
|
||||
export const GITHUB_LINK = 'https://github.com/twentyhq/twenty';
|
||||
@ -1,6 +1,4 @@
|
||||
import { IconComponent } from 'twenty-ui';
|
||||
|
||||
import { Toggle, ToggleSize } from '@/ui/input/components/Toggle';
|
||||
import { IconComponent, Toggle, ToggleSize } from 'twenty-ui';
|
||||
|
||||
import { MenuItemLeftContent } from '../internals/components/MenuItemLeftContent';
|
||||
import {
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { IconX } from 'twenty-ui';
|
||||
import { IconX, UndecoratedLink } from 'twenty-ui';
|
||||
|
||||
import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink';
|
||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
|
||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import {
|
||||
GithubVersionLink,
|
||||
IconAt,
|
||||
IconBell,
|
||||
IconBuildingSkyscraper,
|
||||
@ -18,18 +19,17 @@ import {
|
||||
|
||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { GithubVersionLink } from '@/ui/navigation/link/components/GithubVersionLink';
|
||||
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
|
||||
import { CurrentWorkspaceMemberFavorites } from '@/favorites/components/CurrentWorkspaceMemberFavorites';
|
||||
import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
|
||||
import jsonPage from '../../../../../../../package.json';
|
||||
import { NavigationDrawer } from '../NavigationDrawer';
|
||||
import { NavigationDrawerItem } from '../NavigationDrawerItem';
|
||||
import { NavigationDrawerItemGroup } from '../NavigationDrawerItemGroup';
|
||||
import { NavigationDrawerSection } from '../NavigationDrawerSection';
|
||||
import { NavigationDrawerSectionTitle } from '../NavigationDrawerSectionTitle';
|
||||
|
||||
const meta: Meta<typeof NavigationDrawer> = {
|
||||
title: 'UI/Navigation/NavigationDrawer/NavigationDrawer',
|
||||
component: NavigationDrawer,
|
||||
@ -148,6 +148,6 @@ export const Settings: Story = {
|
||||
</NavigationDrawerSection>
|
||||
</>
|
||||
),
|
||||
footer: <GithubVersionLink />,
|
||||
footer: <GithubVersionLink version={jsonPage.version} />,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user