removed @chakra-ui dependencies (#7004)

Issue #6976 
@FelixMalfait 

I could not do
```
import { Banner } from 'twenty-ui';

const StyledBanner = styled(Banner)
  display: flex;
  align-items: center;
  padding: ${({ theme }) => theme.spacing(8)};
  position: absolute;
  border-radius: 8px;
  &:hover {
    background-color: ${({ theme }) => theme.accent.primary};
  }
;
```
The styles wont get overridden for Banner, so for now I styled a new
banner in `UnmatchColumnBanner` which is inconsistent.
I couldnt figure out why css properties are not being overridden, need
help!

@Bonapara 
Question - 
Should the click work on entire banner or just cheveron? For now it just
on cheveron click.


https://github.com/user-attachments/assets/0f409e78-a341-4f26-af74-117e4b2775a9

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
nitin
2024-09-14 20:25:17 +05:30
committed by GitHub
parent 4544114109
commit 0dbd4a7665
10 changed files with 231 additions and 350 deletions

View File

@ -1,56 +1,13 @@
import { useSpreadsheetImportInternal } from '@/spreadsheet-import/hooks/useSpreadsheetImportInternal';
import { SubMatchingSelect } from '@/spreadsheet-import/steps/components/MatchColumnsStep/components/SubMatchingSelect';
import { UnmatchColumnBanner } from '@/spreadsheet-import/steps/components/MatchColumnsStep/components/UnmatchColumnBanner';
import { Column } from '@/spreadsheet-import/steps/components/MatchColumnsStep/MatchColumnsStep';
import { Fields } from '@/spreadsheet-import/types';
import {
Accordion,
AccordionIcon,
AccordionItem,
AccordionPanel,
AccordionButton as ChakraAccordionButton,
} from '@chakra-ui/accordion';
import styled from '@emotion/styled';
import { IconChevronDown, IconInfoCircle, isDefined } from 'twenty-ui';
import { useState } from 'react';
import { ExpandableContainer, isDefined } from 'twenty-ui';
const StyledAccordionButton = styled(ChakraAccordionButton)`
align-items: center;
background-color: ${({ theme }) => theme.accent.secondary};
border: none;
border-radius: ${({ theme }) => theme.border.radius.md};
box-sizing: border-box;
color: ${({ theme }) => theme.font.color.primary};
display: flex;
flex-direction: row;
padding: ${({ theme }) => theme.spacing(2)};
width: 100%;
height: 40px;
&:hover {
background-color: ${({ theme }) => theme.accent.primary};
}
`;
const StyledAccordionContainer = styled.div`
display: flex;
width: 100%;
height: auto;
`;
const StyledAccordionLabel = styled.span`
color: ${({ theme }) => theme.color.blue};
display: flex;
flex: 1;
font-size: ${({ theme }) => theme.font.size.sm};
align-items: center;
gap: ${({ theme }) => theme.spacing(2)};
text-align: left;
`;
const StyledIconChevronDown = styled(IconChevronDown)`
color: ${({ theme }) => theme.color.blue} !important;
`;
const getAccordionTitle = <T extends string>(
const getExpandableContainerTitle = <T extends string>(
fields: Fields<T>,
column: Column<T>,
) => {
@ -70,42 +27,51 @@ type UnmatchColumnProps<T extends string> = {
onSubChange: (val: T, index: number, option: string) => void;
};
const StyledContainer = styled.div`
position: relative;
width: 100%;
`;
const StyledContentWrapper = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
margin-top: ${({ theme }) => theme.spacing(4)};
padding-bottom: ${({ theme }) => theme.spacing(4)};
`;
export const UnmatchColumn = <T extends string>({
columns,
columnIndex,
onSubChange,
}: UnmatchColumnProps<T>) => {
const { fields } = useSpreadsheetImportInternal<T>();
const [isExpanded, setIsExpanded] = useState(false);
const column = columns[columnIndex];
const isSelect = 'matchedOptions' in column;
if (!isSelect) return null;
return (
isSelect && (
<StyledAccordionContainer>
<Accordion allowMultiple width="100%" height="100%">
<AccordionItem border="none" py={1} height="100%">
<StyledAccordionButton data-testid="accordion-button">
<StyledAccordionLabel>
<IconInfoCircle />
{getAccordionTitle(fields, column)}
</StyledAccordionLabel>
<AccordionIcon as={StyledIconChevronDown} />
</StyledAccordionButton>
<AccordionPanel mt={16} gap={12} display="flex" flexDir="column">
{column.matchedOptions.map((option) => (
<SubMatchingSelect
option={option}
column={column}
onSubChange={onSubChange}
key={option.entry}
placeholder="Select an option"
/>
))}
</AccordionPanel>
</AccordionItem>
</Accordion>
</StyledAccordionContainer>
)
<StyledContainer>
<UnmatchColumnBanner
message={getExpandableContainerTitle(fields, column)}
buttonOnClick={() => setIsExpanded(!isExpanded)}
isExpanded={isExpanded}
/>
<ExpandableContainer isExpanded={isExpanded}>
<StyledContentWrapper>
{column.matchedOptions.map((option) => (
<SubMatchingSelect
option={option}
column={column}
onSubChange={onSubChange}
key={option.entry}
placeholder="Select an option"
/>
))}
</StyledContentWrapper>
</ExpandableContainer>
</StyledContainer>
);
};

View File

@ -0,0 +1,53 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { Banner, IconChevronDown, IconInfoCircle } from 'twenty-ui';
const StyledBanner = styled(Banner)`
background: ${({ theme }) => theme.accent.secondary};
border-radius: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2) + ' ' + theme.spacing(2.5)};
`;
const StyledText = styled.div`
color: ${({ theme }) => theme.color.blue};
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
const StyledTransitionedIconChevronDown = styled(IconChevronDown)<{
isExpanded: boolean;
}>`
color: ${({ theme }) => theme.color.blue};
transform: ${({ isExpanded }) =>
isExpanded ? 'rotate(180deg)' : 'rotate(0deg)'};
transition: ${({ theme }) =>
`transform ${theme.animation.duration.normal}s ease`};
cursor: pointer;
`;
export const UnmatchColumnBanner = ({
message,
isExpanded,
buttonOnClick,
}: {
message: string;
isExpanded: boolean;
buttonOnClick?: () => void;
}) => {
const theme = useTheme();
return (
<StyledBanner>
<IconInfoCircle color={theme.color.blue} size={theme.icon.size.md} />
<StyledText>{message}</StyledText>
{buttonOnClick && (
<StyledTransitionedIconChevronDown
isExpanded={isExpanded}
onClick={buttonOnClick}
size={theme.icon.size.md}
/>
)}
</StyledBanner>
);
};

View File

@ -127,7 +127,6 @@ export default defineConfig(({ command, mode }) => {
localsConvention: 'camelCaseOnly',
},
},
resolve: {
alias: {
path: 'rollup-plugin-node-polyfills/polyfills/path',

View File

@ -22,12 +22,18 @@ const StyledBanner = styled.div<{ variant?: BannerVariant }>`
export type BannerVariant = 'danger' | 'default';
type BannerProps = {
variant?: BannerVariant;
className?: string;
children: React.ReactNode;
} & React.HTMLAttributes<HTMLDivElement>;
export const Banner = ({
variant = 'default',
className,
children,
}: {
variant?: BannerVariant;
children: React.ReactNode;
} & React.HTMLAttributes<HTMLDivElement>) => (
<StyledBanner variant={variant}>{children}</StyledBanner>
}: BannerProps) => (
<StyledBanner variant={variant} className={className}>
{children}
</StyledBanner>
);

View File

@ -1,5 +1,6 @@
export * from './components';
export * from './display';
export * from './layout';
export * from './testing';
export * from './theme';
export * from './utilities';

View File

@ -0,0 +1,42 @@
import styled from '@emotion/styled';
import { isDefined } from '@ui/utilities';
import React, { useLayoutEffect, useRef, useState } from 'react';
const StyledTransitionContainer = styled.div<{
isExpanded: boolean;
height: number;
}>`
max-height: ${({ isExpanded, height }) => (isExpanded ? `${height}px` : '0')};
overflow: hidden;
position: relative;
transition: max-height
${({ theme, isExpanded }) =>
`${theme.animation.duration.normal}s ${isExpanded ? 'ease-in' : 'ease-out'}`};
`;
type ExpandableContainerProps = {
isExpanded: boolean;
children: React.ReactNode;
};
export const ExpandableContainer = ({
isExpanded,
children,
}: ExpandableContainerProps) => {
const [contentHeight, setContentHeight] = useState(0);
const contentRef = useRef<HTMLDivElement>(null);
useLayoutEffect(() => {
if (isDefined(contentRef.current)) {
setContentHeight(contentRef.current.scrollHeight);
}
}, [isExpanded]);
return (
<StyledTransitionContainer isExpanded={isExpanded} height={contentHeight}>
<div ref={contentRef}>{children}</div>
</StyledTransitionContainer>
);
};
export default ExpandableContainer;

View File

@ -0,0 +1,81 @@
import styled from '@emotion/styled';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '@ui/testing';
import { useState } from 'react';
import ExpandableContainer from '../ExpandableContainer';
const StyledButton = styled.button`
padding: ${({ theme }) => theme.spacing(2)} ${({ theme }) => theme.spacing(4)};
background-color: ${({ theme }) => theme.color.blue50};
color: ${({ theme }) => theme.font.color.primary};
border: none;
border-radius: ${({ theme }) => theme.spacing(1)};
cursor: pointer;
margin-bottom: ${({ theme }) => theme.spacing(3)};
&:hover {
background-color: ${({ theme }) => theme.color.blue40};
}
`;
const StyledContent = styled.div`
background-color: ${({ theme }) => theme.background.primary};
height: 200px;
padding: ${({ theme }) => theme.spacing(3)};
p {
color: ${({ theme }) => theme.font.color.primary};
margin-bottom: ${({ theme }) => theme.spacing(2)};
font-size: ${({ theme }) => theme.font.size.md};
}
`;
const ExpandableContainerWithButton = (args: any) => {
const [isExpanded, setIsExpanded] = useState(args.isExpanded);
return (
<div>
<StyledButton onClick={() => setIsExpanded(!isExpanded)}>
{isExpanded ? 'Collapse' : 'Expand'}
</StyledButton>
<ExpandableContainer isExpanded={isExpanded}>
<StyledContent>
<p>
This is some content inside the ExpandableContainer. It will grow
and shrink depending on the expand/collapse state.
</p>
<p>
Add more text or even other components here to test how the
container handles more content.
</p>
<p>
Feel free to adjust the height and content to see how it affects the
expand/collapse behavior.
</p>
</StyledContent>
</ExpandableContainer>
</div>
);
};
const meta: Meta<typeof ExpandableContainer> = {
title: 'UI/Layout/ExpandableContainer',
component: ExpandableContainerWithButton,
decorators: [ComponentDecorator],
argTypes: {
isExpanded: {
control: 'boolean',
description: 'Controls whether the container is expanded or collapsed',
defaultValue: false,
},
},
};
export default meta;
type Story = StoryObj<typeof ExpandableContainerWithButton>;
export const Default: Story = {
args: {
isExpanded: false,
},
};

View File

@ -0,0 +1 @@
export * from './expandableContainer/components/ExpandableContainer';