Remove all styled(Component) patterns in favor of parent wrappers and props (#18430)
## Summary
Eliminates all ~350 `styled(Component)` usages across `twenty-front` and
`twenty-ui` (212 files changed). Each was replaced following these
rules:
- **Margin/layout CSS** (margin, padding, flex, align-self, width) →
wrapped in a `styled.div`/`styled.span` parent container
- **Third-party components** (Link, TextareaAutosize,
ReactPhoneNumberInput, Handle, etc.) → parent container with child CSS
selectors (`> a`, `> textarea`, `> input`, etc.)
- **Intrinsic behavior via existing props** (TableRow
`gridTemplateColumns`, TableCell `color`/`align`) → replaced
`styled(TableRow)` / `styled(TableCell)` with direct prop usage
- **Other visual overrides on twenty-ui components** (Card, Section,
TabList, Button, MenuItem, ScrollWrapper, etc.) → parent wrappers with
`> div` / `> *` child selectors
- **Extending styled.div/span** → merged all CSS into a single
`styled.div`/`styled.span`
Also adds `overflow: hidden` to parent containers wrapping
`ScrollWrapper` so scroll activates correctly with the new wrapper
structure.
### Migration patterns
| Before | After |
|--------|-------|
| `styled(Avatar)` with `margin-right` | `<StyledAvatarContainer><Avatar
/></StyledAvatarContainer>` |
| `styled(Link)` with `text-decoration: none` |
`<StyledLinkContainer><Link /></StyledLinkContainer>` with `> a { ... }`
|
| `styled(TableRow)` with `grid-template-columns` | `<TableRow
gridTemplateColumns="..." />` |
| `styled(TableCell)` with `color` / `align` | `<TableCell color={...}
align="right" />` |
| `styled(Card)` with `margin-top` | `<StyledCardContainer><Card
/></StyledCardContainer>` |
| `styled(TabList)` with `background` |
`<StyledTabListContainer><TabList /></StyledTabListContainer>` with `>
div { ... }` |
| `styled(StyledBase)` extending a `styled.div` | Single merged
`styled.div` with all styles inlined |
This commit is contained in:
+13
-11
@@ -7,7 +7,7 @@ import { IconFilter } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
const StyledButton = styled(Button)`
|
||||
const StyledButtonContainer = styled.div`
|
||||
margin-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
@@ -17,15 +17,17 @@ export const WorkflowStepFilterAddRootStepFilterButton = () => {
|
||||
const { addRootStepFilter } = useAddRootStepFilter();
|
||||
|
||||
return (
|
||||
<StyledButton
|
||||
Icon={IconFilter}
|
||||
size="small"
|
||||
variant="secondary"
|
||||
accent="default"
|
||||
onClick={addRootStepFilter}
|
||||
ariaLabel={t`Add first filter`}
|
||||
title={t`Add first filter`}
|
||||
disabled={readonly}
|
||||
/>
|
||||
<StyledButtonContainer>
|
||||
<Button
|
||||
Icon={IconFilter}
|
||||
size="small"
|
||||
variant="secondary"
|
||||
accent="default"
|
||||
onClick={addRootStepFilter}
|
||||
ariaLabel={t`Add first filter`}
|
||||
title={t`Add first filter`}
|
||||
disabled={readonly}
|
||||
/>
|
||||
</StyledButtonContainer>
|
||||
);
|
||||
};
|
||||
|
||||
+21
-2
@@ -1,4 +1,5 @@
|
||||
import { styled } from '@linaria/react';
|
||||
import React from 'react';
|
||||
import { IconChevronRight, Label } from 'twenty-ui/display';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
|
||||
@@ -6,11 +7,17 @@ export const StyledText = styled.div`
|
||||
color: ${themeCssVariables.font.color.secondary};
|
||||
`;
|
||||
|
||||
export const StyledLabel = styled(Label)`
|
||||
const StyledLabelWrapper = styled.div`
|
||||
margin: ${themeCssVariables.spacing[3]} ${themeCssVariables.spacing[3]}
|
||||
${themeCssVariables.spacing[0]};
|
||||
`;
|
||||
|
||||
export const StyledLabel = ({ children }: { children: React.ReactNode }) => (
|
||||
<StyledLabelWrapper>
|
||||
<Label>{children}</Label>
|
||||
</StyledLabelWrapper>
|
||||
);
|
||||
|
||||
export const StyledRow = styled.div<{ isDisabled?: boolean }>`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@@ -61,6 +68,18 @@ export const StyledIconContainer = styled.div`
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
`;
|
||||
|
||||
export const StyledIconChevronRight = styled(IconChevronRight)`
|
||||
const StyledIconChevronRightWrapper = styled.div`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: inline-flex;
|
||||
`;
|
||||
|
||||
export const StyledIconChevronRight = ({
|
||||
size,
|
||||
color,
|
||||
stroke,
|
||||
}: React.ComponentProps<typeof IconChevronRight>) => (
|
||||
<StyledIconChevronRightWrapper>
|
||||
<IconChevronRight size={size} color={color} stroke={stroke} />
|
||||
</StyledIconChevronRightWrapper>
|
||||
);
|
||||
+14
-12
@@ -26,11 +26,11 @@ import { WorkflowAiAgentPermissionsFlagList } from './WorkflowAiAgentPermissions
|
||||
import { WorkflowAiAgentPermissionsObjectsList } from './WorkflowAiAgentPermissionsObjectsList';
|
||||
import { getFilteredPermissions } from './workflowAiAgentPermissions.utils';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
const StyledSearchInput = styled(TextInput)`
|
||||
const StyledSearchInputContainer = styled.div`
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
border-block: 1px solid ${themeCssVariables.border.color.medium};
|
||||
input {
|
||||
& input {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
border: none;
|
||||
@@ -198,16 +198,18 @@ export const WorkflowAiAgentPermissionsTab = ({
|
||||
</StyledBackButton>
|
||||
)}
|
||||
|
||||
<StyledSearchInput
|
||||
value={searchQuery}
|
||||
onChange={(value: string) => setSearchQuery(value)}
|
||||
placeholder={t`Type anything...`}
|
||||
onKeyDown={(event) => {
|
||||
if (isNonTextWritingKey(event.key)) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<StyledSearchInputContainer>
|
||||
<TextInput
|
||||
value={searchQuery}
|
||||
onChange={(value: string) => setSearchQuery(value)}
|
||||
placeholder={t`Type anything...`}
|
||||
onKeyDown={(event) => {
|
||||
if (isNonTextWritingKey(event.key)) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</StyledSearchInputContainer>
|
||||
|
||||
{shouldShowCrudList && (
|
||||
<WorkflowAiAgentPermissionsCrudList
|
||||
|
||||
+8
-6
@@ -50,7 +50,7 @@ type WorkflowEditActionAiAgentProps = {
|
||||
};
|
||||
};
|
||||
|
||||
const StyledTabList = styled(TabList)`
|
||||
const StyledTabListContainer = styled.div`
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
@@ -270,11 +270,13 @@ export const WorkflowEditActionAiAgent = ({
|
||||
<SidePanelSkeletonLoader />
|
||||
) : (
|
||||
<>
|
||||
<StyledTabList
|
||||
tabs={tabs}
|
||||
componentInstanceId={componentInstanceId}
|
||||
behaveAsLinks={false}
|
||||
/>
|
||||
<StyledTabListContainer>
|
||||
<TabList
|
||||
tabs={tabs}
|
||||
componentInstanceId={componentInstanceId}
|
||||
behaveAsLinks={false}
|
||||
/>
|
||||
</StyledTabListContainer>
|
||||
{currentTabId === WORKFLOW_AI_AGENT_TABS.PERMISSIONS ? (
|
||||
<WorkflowStepBody paddingBlock="0" paddingInline="0">
|
||||
<WorkflowAiAgentPermissionsTab
|
||||
|
||||
+8
-6
@@ -55,7 +55,7 @@ const StyledCodeEditorContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const StyledTabList = styled(TabList)`
|
||||
const StyledTabListContainer = styled.div`
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
@@ -370,11 +370,13 @@ export const WorkflowEditActionCode = ({
|
||||
!loading && (
|
||||
<>
|
||||
<LogicFunctionTestInputInitEffect logicFunctionId={logicFunctionId} />
|
||||
<StyledTabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID}
|
||||
/>
|
||||
<StyledTabListContainer>
|
||||
<TabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID}
|
||||
/>
|
||||
</StyledTabListContainer>
|
||||
<WorkflowStepBody>
|
||||
{activeTabId === WorkflowLogicFunctionTabId.CODE && (
|
||||
<>
|
||||
|
||||
+4
-1
@@ -139,8 +139,11 @@ const StyledCalloutContainer = styled.div`
|
||||
padding-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledNotClosableCalloutContainer = styled(StyledCalloutContainer)`
|
||||
const StyledNotClosableCalloutContainer = styled.div`
|
||||
padding-bottom: ${themeCssVariables.spacing[4]};
|
||||
padding-left: ${themeCssVariables.spacing[7]};
|
||||
padding-right: ${themeCssVariables.spacing[7]};
|
||||
padding-top: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
export const WorkflowEditActionFormBuilder = ({
|
||||
|
||||
+20
-18
@@ -32,7 +32,7 @@ const StyledContainer = styled.div`
|
||||
gap: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
|
||||
const StyledSelectDropdown = styled(Select)`
|
||||
const StyledSelectDropdownContainer = styled.div`
|
||||
margin-bottom: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
const StyledNoBodyMessage = styled.div`
|
||||
@@ -163,23 +163,25 @@ export const BodyInput = ({
|
||||
return (
|
||||
<FormFieldInputContainer>
|
||||
<InputLabel>{t`Body Input`}</InputLabel>
|
||||
<StyledSelectDropdown
|
||||
options={[
|
||||
{ label: t`Key/Value`, value: BODY_TYPES.KEY_VALUE, Icon: IconKey },
|
||||
{
|
||||
label: t`Raw JSON`,
|
||||
value: BODY_TYPES.RAW_JSON,
|
||||
Icon: IconFileText,
|
||||
},
|
||||
{ label: t`Form Data`, value: BODY_TYPES.FORM_DATA, Icon: IconKey },
|
||||
{ label: t`Text`, value: BODY_TYPES.TEXT, Icon: IconFileText },
|
||||
{ label: t`None`, value: BODY_TYPES.NONE, Icon: IconFileText },
|
||||
]}
|
||||
dropdownId="body-input-mode"
|
||||
value={getBodyTypeFromHeaders(headers) || BODY_TYPES.NONE}
|
||||
onChange={(value) => handleModeChange(value as BodyType)}
|
||||
disabled={readonly}
|
||||
/>
|
||||
<StyledSelectDropdownContainer>
|
||||
<Select
|
||||
options={[
|
||||
{ label: t`Key/Value`, value: BODY_TYPES.KEY_VALUE, Icon: IconKey },
|
||||
{
|
||||
label: t`Raw JSON`,
|
||||
value: BODY_TYPES.RAW_JSON,
|
||||
Icon: IconFileText,
|
||||
},
|
||||
{ label: t`Form Data`, value: BODY_TYPES.FORM_DATA, Icon: IconKey },
|
||||
{ label: t`Text`, value: BODY_TYPES.TEXT, Icon: IconFileText },
|
||||
{ label: t`None`, value: BODY_TYPES.NONE, Icon: IconFileText },
|
||||
]}
|
||||
dropdownId="body-input-mode"
|
||||
value={getBodyTypeFromHeaders(headers) || BODY_TYPES.NONE}
|
||||
onChange={(value) => handleModeChange(value as BodyType)}
|
||||
disabled={readonly}
|
||||
/>
|
||||
</StyledSelectDropdownContainer>
|
||||
|
||||
<StyledContainer>
|
||||
{isBodyTypeRawJson ? (
|
||||
|
||||
+19
-15
@@ -39,7 +39,7 @@ type WorkflowEditActionHttpRequestProps = {
|
||||
};
|
||||
};
|
||||
|
||||
const StyledTabList = styled(TabList)`
|
||||
const StyledTabListContainer = styled.div`
|
||||
background-color: ${themeCssVariables.background.secondary};
|
||||
padding-left: ${themeCssVariables.spacing[2]};
|
||||
`;
|
||||
@@ -61,7 +61,7 @@ const StyledConfigurationTabContent = styled.div`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const StyledFullHeightFormRawJsonFieldInput = styled(FormRawJsonFieldInput)`
|
||||
const StyledFullHeightFormRawJsonFieldInputContainer = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -128,11 +128,13 @@ export const WorkflowEditActionHttpRequest = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledTabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={WORKFLOW_HTTP_REQUEST_TAB_LIST_COMPONENT_ID}
|
||||
/>
|
||||
<StyledTabListContainer>
|
||||
<TabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={WORKFLOW_HTTP_REQUEST_TAB_LIST_COMPONENT_ID}
|
||||
/>
|
||||
</StyledTabListContainer>
|
||||
<WorkflowStepBody>
|
||||
{activeTabId === WorkflowHttpRequestTabId.CONFIGURATION && (
|
||||
<StyledConfigurationTabContent>
|
||||
@@ -178,14 +180,16 @@ export const WorkflowEditActionHttpRequest = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
<StyledFullHeightFormRawJsonFieldInput
|
||||
label={t`Expected Response Body`}
|
||||
placeholder={JSON_RESPONSE_PLACEHOLDER}
|
||||
defaultValue={outputSchema}
|
||||
onChange={handleOutputSchemaChange}
|
||||
readonly={actionOptions.readonly}
|
||||
error={error}
|
||||
/>
|
||||
<StyledFullHeightFormRawJsonFieldInputContainer>
|
||||
<FormRawJsonFieldInput
|
||||
label={t`Expected Response Body`}
|
||||
placeholder={JSON_RESPONSE_PLACEHOLDER}
|
||||
defaultValue={outputSchema}
|
||||
onChange={handleOutputSchemaChange}
|
||||
readonly={actionOptions.readonly}
|
||||
error={error}
|
||||
/>
|
||||
</StyledFullHeightFormRawJsonFieldInputContainer>
|
||||
</StyledConfigurationTabContent>
|
||||
)}
|
||||
{activeTabId === WorkflowHttpRequestTabId.TEST && (
|
||||
|
||||
Reference in New Issue
Block a user