Rename twenty-ui to twenty-ui-deprecated and twenty-new-ui to twenty-ui to prepare package release (#21315)
## Description Promotes the next-gen UI library (formerly `twenty-new-ui`) to the name **`twenty-ui`** (v0.1.0, publishable) and renames the old package to **`twenty-ui-deprecated`**. Rewrites ~1,730 `twenty-ui` imports → `twenty-ui-deprecated`, updates all configs/CI/Docker/deps, and migrates twenty-front's `Toggle` to the new package (first consumer) as a drop-in. ## Next steps - Wire the `ui/v*` publish dispatch (`cd-deploy-tag.yaml` + `.yarnrc.yml`), then tag `ui/v0.1.0` to publish. - Continue migrating components from `twenty-ui-deprecated` → `twenty-ui`.
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from '@ui/theme-constants';
|
||||
|
||||
const StyledLayout = styled.div<{
|
||||
width?: number;
|
||||
backgroundColor?: string | undefined;
|
||||
height: number | 'fit-content';
|
||||
}>`
|
||||
background: ${({ backgroundColor }) =>
|
||||
backgroundColor ?? themeCssVariables.background.primary};
|
||||
border: 1px solid ${themeCssVariables.border.color.light};
|
||||
border-radius: 5px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
height: ${({ height }) =>
|
||||
height === 'fit-content'
|
||||
? 'fit-content'
|
||||
: `
|
||||
${height}px
|
||||
`};
|
||||
max-width: calc(100% - 40px);
|
||||
min-width: ${({ width }) => (width ? 'unset' : '300px')};
|
||||
padding: 20px;
|
||||
width: ${({ width }) => (width ? width + 'px' : 'fit-content')};
|
||||
`;
|
||||
|
||||
type ComponentStorybookLayoutProps = {
|
||||
width?: number;
|
||||
backgroundColor?: string | undefined;
|
||||
height?: number;
|
||||
children: JSX.Element;
|
||||
};
|
||||
|
||||
export const ComponentStorybookLayout = ({
|
||||
width,
|
||||
backgroundColor,
|
||||
height,
|
||||
children,
|
||||
}: ComponentStorybookLayoutProps) => {
|
||||
return (
|
||||
<StyledLayout
|
||||
width={width}
|
||||
backgroundColor={backgroundColor}
|
||||
height={isDefined(height) ? height : 'fit-content'}
|
||||
>
|
||||
{children}
|
||||
</StyledLayout>
|
||||
);
|
||||
};
|
||||
@@ -1,172 +0,0 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import { isNumber, isString } from '@sniptt/guards';
|
||||
import { type Decorator } from '@storybook/react-vite';
|
||||
import { type ComponentProps, type JSX } from 'react';
|
||||
import { themeCssVariables } from '@ui/theme-constants';
|
||||
|
||||
const StyledColumnTitle = styled.h1`
|
||||
font-size: ${themeCssVariables.font.size.lg};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
margin: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledRowsTitle = styled.h2`
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
margin: ${themeCssVariables.spacing[2]};
|
||||
width: 100px;
|
||||
`;
|
||||
|
||||
const StyledRowTitle = styled.h3`
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
font-size: ${themeCssVariables.font.size.md};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
margin: ${themeCssVariables.spacing[2]};
|
||||
width: 100px;
|
||||
`;
|
||||
|
||||
const StyledElementTitle = styled.span`
|
||||
color: ${themeCssVariables.font.color.light};
|
||||
font-size: ${themeCssVariables.font.size.xs};
|
||||
font-weight: ${themeCssVariables.font.weight.semiBold};
|
||||
margin-bottom: ${themeCssVariables.spacing[1]};
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
`;
|
||||
|
||||
const StyledColumnContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledRowsContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledRowContainer = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledElementContainer = styled.div<{ width: number }>`
|
||||
display: flex;
|
||||
min-width: ${({ width }) => (width ? `${width}px` : 'auto')};
|
||||
`;
|
||||
|
||||
const StyledCellContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const emptyDimension = {
|
||||
name: '',
|
||||
values: [undefined],
|
||||
props: () => ({}),
|
||||
} as CatalogDimension;
|
||||
|
||||
const isStringOrNumber = (term: unknown): term is string | number =>
|
||||
isString(term) || isNumber(term);
|
||||
|
||||
export type CatalogDimension<
|
||||
ComponentType extends React.ElementType = () => JSX.Element,
|
||||
> = {
|
||||
name: string;
|
||||
values: any[];
|
||||
props: (value: any) => Partial<ComponentProps<ComponentType>>;
|
||||
labels?: (value: any) => string;
|
||||
};
|
||||
|
||||
export type CatalogOptions = {
|
||||
elementContainer?: {
|
||||
width?: number;
|
||||
style?: React.CSSProperties;
|
||||
className?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const CatalogDecorator: Decorator = (Story, context) => {
|
||||
const {
|
||||
catalog: { dimensions = [], options = {} } = {
|
||||
dimensions: [],
|
||||
options: {},
|
||||
},
|
||||
} = context.parameters || {};
|
||||
|
||||
if (!dimensions || !Array.isArray(dimensions)) {
|
||||
return <Story />;
|
||||
}
|
||||
|
||||
const [
|
||||
dimension1 = emptyDimension,
|
||||
dimension2 = emptyDimension,
|
||||
dimension3 = emptyDimension,
|
||||
dimension4 = emptyDimension,
|
||||
] = dimensions as CatalogDimension[];
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
{dimension4.values.map((value4: any, index4: number) => (
|
||||
<StyledColumnContainer key={`d4-${index4}`}>
|
||||
<StyledColumnTitle>
|
||||
{dimension4.labels?.(value4) ??
|
||||
(isStringOrNumber(value4) ? value4 : '')}
|
||||
</StyledColumnTitle>
|
||||
{dimension3.values.map((value3: any, index3: number) => (
|
||||
<StyledRowsContainer key={`d3-${index3}`}>
|
||||
<StyledRowsTitle>
|
||||
{dimension3.labels?.(value3) ??
|
||||
(isStringOrNumber(value3) ? value3 : '')}
|
||||
</StyledRowsTitle>
|
||||
{dimension2.values.map((value2: any, index2: number) => (
|
||||
<StyledRowContainer key={`d2-${index2}`}>
|
||||
<StyledRowTitle>
|
||||
{dimension2.labels?.(value2) ??
|
||||
(isStringOrNumber(value2) ? value2 : '')}
|
||||
</StyledRowTitle>
|
||||
{dimension1.values.map((value1: any, index1: number) => {
|
||||
return (
|
||||
<StyledCellContainer key={`d1-${index1}`} id={value1}>
|
||||
<StyledElementTitle>
|
||||
{dimension1.labels?.(value1) ??
|
||||
(isStringOrNumber(value1) ? value1 : '')}
|
||||
</StyledElementTitle>
|
||||
<StyledElementContainer
|
||||
width={options?.elementContainer?.width}
|
||||
style={options?.elementContainer?.style}
|
||||
className={options?.elementContainer?.className}
|
||||
>
|
||||
<Story
|
||||
args={{
|
||||
...context.args,
|
||||
...dimension1.props(value1),
|
||||
...dimension2.props(value2),
|
||||
...dimension3.props(value3),
|
||||
...dimension4.props(value4),
|
||||
}}
|
||||
/>
|
||||
</StyledElementContainer>
|
||||
</StyledCellContainer>
|
||||
);
|
||||
})}
|
||||
</StyledRowContainer>
|
||||
))}
|
||||
</StyledRowsContainer>
|
||||
))}
|
||||
</StyledColumnContainer>
|
||||
))}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
import { type Decorator } from '@storybook/react-vite';
|
||||
|
||||
import { themeCssVariables } from '@ui/theme-constants';
|
||||
|
||||
import { ComponentStorybookLayout } from '../ComponentStorybookLayout';
|
||||
|
||||
const getBackgroundColor = (
|
||||
inverted: boolean,
|
||||
accent: string,
|
||||
): string | undefined => {
|
||||
if (!inverted) return undefined;
|
||||
|
||||
switch (accent) {
|
||||
case 'default':
|
||||
return themeCssVariables.grayScale.gray11;
|
||||
case 'danger':
|
||||
return themeCssVariables.color.red;
|
||||
case 'blue':
|
||||
return themeCssVariables.color.blue;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const ComponentDecorator: Decorator = (Story, context) => {
|
||||
const { container } = context.parameters;
|
||||
const inverted = context.args.inverted as boolean;
|
||||
const accent = context.args.accent as string;
|
||||
const backgroundColor = getBackgroundColor(inverted, accent);
|
||||
|
||||
return (
|
||||
<ComponentStorybookLayout
|
||||
width={container?.width}
|
||||
height={container?.height}
|
||||
backgroundColor={backgroundColor}
|
||||
>
|
||||
<Story />
|
||||
</ComponentStorybookLayout>
|
||||
);
|
||||
};
|
||||
@@ -1,82 +0,0 @@
|
||||
import { type Decorator } from '@storybook/react-vite';
|
||||
import {
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
Outlet,
|
||||
Route,
|
||||
RouterProvider,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import { ComponentStorybookLayout } from '../ComponentStorybookLayout';
|
||||
|
||||
interface StrictArgs {
|
||||
[name: string]: unknown;
|
||||
}
|
||||
export type RouteParams = {
|
||||
[param: string]: string;
|
||||
};
|
||||
|
||||
export const isRouteParams = (obj: any): obj is RouteParams => {
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Object.keys(obj).every((key) => typeof obj[key] === 'string');
|
||||
};
|
||||
|
||||
export const computeLocation = (
|
||||
routePath: string,
|
||||
routeParams?: RouteParams,
|
||||
) => {
|
||||
return {
|
||||
pathname: routePath.replace(
|
||||
/:(\w+)/g,
|
||||
(paramName) => routeParams?.[paramName] ?? '',
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const Providers = () => (
|
||||
<ComponentStorybookLayout>
|
||||
<Outlet />
|
||||
</ComponentStorybookLayout>
|
||||
);
|
||||
|
||||
const createRouter = ({
|
||||
Story,
|
||||
args,
|
||||
initialEntries,
|
||||
initialIndex,
|
||||
}: {
|
||||
Story: () => JSX.Element;
|
||||
args: StrictArgs;
|
||||
initialEntries?: {
|
||||
pathname: string;
|
||||
}[];
|
||||
initialIndex?: number;
|
||||
}) =>
|
||||
createMemoryRouter(
|
||||
createRoutesFromElements(
|
||||
<Route element={<Providers />}>
|
||||
<Route path={(args.routePath as string) ?? '*'} element={<Story />} />
|
||||
</Route>,
|
||||
),
|
||||
{ initialEntries, initialIndex },
|
||||
);
|
||||
|
||||
export const ComponentWithRouterDecorator: Decorator = (Story, { args }) => {
|
||||
return (
|
||||
<RouterProvider
|
||||
router={createRouter({
|
||||
Story,
|
||||
args,
|
||||
initialEntries:
|
||||
args.routePath &&
|
||||
typeof args.routePath === 'string' &&
|
||||
(args.routeParams === undefined || isRouteParams(args.routeParams))
|
||||
? [computeLocation(args.routePath, args.routeParams)]
|
||||
: [{ pathname: '/' }],
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,9 +0,0 @@
|
||||
import { type Decorator } from '@storybook/react-vite';
|
||||
|
||||
import { Provider as JotaiProvider } from 'jotai';
|
||||
|
||||
export const JotaiRootDecorator: Decorator = (Story) => (
|
||||
<JotaiProvider>
|
||||
<Story />
|
||||
</JotaiProvider>
|
||||
);
|
||||
@@ -1,8 +0,0 @@
|
||||
import { type Decorator } from '@storybook/react-vite';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
export const RouterDecorator: Decorator = (Story) => (
|
||||
<MemoryRouter>
|
||||
<Story />
|
||||
</MemoryRouter>
|
||||
);
|
||||
@@ -7,20 +7,4 @@
|
||||
* |___/
|
||||
*/
|
||||
|
||||
export { ComponentStorybookLayout } from './ComponentStorybookLayout';
|
||||
export type {
|
||||
CatalogDimension,
|
||||
CatalogOptions,
|
||||
} from './decorators/CatalogDecorator';
|
||||
export { CatalogDecorator } from './decorators/CatalogDecorator';
|
||||
export { ComponentDecorator } from './decorators/ComponentDecorator';
|
||||
export type { RouteParams } from './decorators/ComponentWithRouterDecorator';
|
||||
export {
|
||||
isRouteParams,
|
||||
computeLocation,
|
||||
ComponentWithRouterDecorator,
|
||||
} from './decorators/ComponentWithRouterDecorator';
|
||||
export { JotaiRootDecorator } from './decorators/JotaiRootDecorator';
|
||||
export { RouterDecorator } from './decorators/RouterDecorator';
|
||||
export { AVATAR_URL_MOCK } from './mocks/avatarUrlMock';
|
||||
export type { CatalogStory } from './types/CatalogStory';
|
||||
export {};
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export const AVATAR_URL_MOCK =
|
||||
'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAYABgAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABgAAAAAQAAAGAAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABSgAwAEAAAAAQAAABQAAAAA/8AAEQgAFAAUAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMACwgICggHCwoJCg0MCw0RHBIRDw8RIhkaFBwpJCsqKCQnJy0yQDctMD0wJyc4TDk9Q0VISUgrNk9VTkZUQEdIRf/bAEMBDA0NEQ8RIRISIUUuJy5FRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRf/dAAQAAv/aAAwDAQACEQMRAD8Ava1q728otYY98joSCTgZrnbXWdTtrhrfVZXWLafmcAEkdgR/hVltQku9Q8+OIEBcGOT+ID0PY1ka1KH2u8ToqnPLbmIqG7u6LtbQ7RXBRec4Uck9eKXcPWsKDWVnhWSL5kYcFelSf2m3901POh8jP//QoyIAnTuKpXsY82NsksUyWPU5q/L9z8RVK++/F/uCsVsaEURwgA4HtT9x9TUcf3KfUGh//9k=';
|
||||
@@ -1,24 +0,0 @@
|
||||
import { type StoryObj } from '@storybook/react-vite';
|
||||
import { type ElementType } from 'react';
|
||||
|
||||
import {
|
||||
type CatalogDimension,
|
||||
type CatalogOptions,
|
||||
} from '../decorators/CatalogDecorator';
|
||||
|
||||
export type CatalogStory<
|
||||
StoryType extends StoryObj<ComponentType>,
|
||||
ComponentType extends ElementType,
|
||||
> = {
|
||||
args?: StoryType['args'];
|
||||
argTypes?: StoryType['argTypes'];
|
||||
play?: StoryType['play'];
|
||||
render?: StoryType['render'];
|
||||
parameters: StoryType['parameters'] & {
|
||||
catalog: {
|
||||
dimensions: CatalogDimension<ComponentType>[];
|
||||
options?: CatalogOptions;
|
||||
};
|
||||
};
|
||||
decorators: StoryType['decorators'];
|
||||
};
|
||||
Reference in New Issue
Block a user