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:
+26
-24
@@ -1,5 +1,3 @@
|
||||
import { styled } from '@linaria/react';
|
||||
|
||||
import {
|
||||
formatExpiration,
|
||||
isExpired,
|
||||
@@ -7,23 +5,11 @@ import {
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { IconChevronRight } from 'twenty-ui/display';
|
||||
import { styled } from '@linaria/react';
|
||||
import { useContext } from 'react';
|
||||
import { MOBILE_VIEWPORT, ThemeContext } from 'twenty-ui/theme-constants';
|
||||
import { ThemeContext } from 'twenty-ui/theme-constants';
|
||||
import { type ApiKey } from '~/generated-metadata/graphql';
|
||||
|
||||
export const StyledApisFieldTableRow = styled(TableRow)`
|
||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTruncatedCell = styled(TableCell)`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const StyledEllipsisLabel = styled.div`
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
@@ -49,24 +35,40 @@ export const SettingsApiKeysFieldItemTableRow = ({
|
||||
const gridColumns = '5fr 2fr 3fr 1fr';
|
||||
|
||||
return (
|
||||
<StyledApisFieldTableRow gridAutoColumns={gridColumns} to={to}>
|
||||
<StyledTruncatedCell color={theme.font.color.primary}>
|
||||
<TableRow gridAutoColumns={gridColumns} to={to}>
|
||||
<TableCell
|
||||
color={theme.font.color.primary}
|
||||
whiteSpace="nowrap"
|
||||
overflow="hidden"
|
||||
textOverflow="ellipsis"
|
||||
clickable
|
||||
>
|
||||
<StyledEllipsisLabel>{apiKey.name}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
</TableCell>
|
||||
|
||||
<StyledTruncatedCell color={theme.font.color.tertiary}>
|
||||
<TableCell
|
||||
color={theme.font.color.tertiary}
|
||||
whiteSpace="nowrap"
|
||||
overflow="hidden"
|
||||
textOverflow="ellipsis"
|
||||
clickable
|
||||
>
|
||||
<StyledEllipsisLabel>{apiKey.role?.label || '-'}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
</TableCell>
|
||||
|
||||
<StyledTruncatedCell
|
||||
<TableCell
|
||||
color={
|
||||
isExpired(apiKey.expiresAt || null)
|
||||
? theme.font.color.danger
|
||||
: theme.font.color.tertiary
|
||||
}
|
||||
whiteSpace="nowrap"
|
||||
overflow="hidden"
|
||||
textOverflow="ellipsis"
|
||||
clickable
|
||||
>
|
||||
<StyledEllipsisLabel>{formattedExpiration}</StyledEllipsisLabel>
|
||||
</StyledTruncatedCell>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="right">
|
||||
<IconChevronRight
|
||||
@@ -74,6 +76,6 @@ export const SettingsApiKeysFieldItemTableRow = ({
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
</TableCell>
|
||||
</StyledApisFieldTableRow>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
+14
-12
@@ -10,7 +10,7 @@ import { getSettingsPath } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { useGetApiKeysQuery } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledTableBody = styled(TableBody)`
|
||||
const StyledTableBodyContainer = styled.div`
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
`;
|
||||
|
||||
@@ -36,17 +36,19 @@ export const SettingsApiKeysTable = () => {
|
||||
<TableHeader></TableHeader>
|
||||
</TableRow>
|
||||
{!!apiKeys?.length && (
|
||||
<StyledTableBody>
|
||||
{apiKeys.map((apiKey) => (
|
||||
<SettingsApiKeysFieldItemTableRow
|
||||
key={apiKey.id}
|
||||
apiKey={apiKey}
|
||||
to={getSettingsPath(SettingsPath.ApiKeyDetail, {
|
||||
apiKeyId: apiKey.id,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
</StyledTableBody>
|
||||
<StyledTableBodyContainer>
|
||||
<TableBody>
|
||||
{apiKeys.map((apiKey) => (
|
||||
<SettingsApiKeysFieldItemTableRow
|
||||
key={apiKey.id}
|
||||
apiKey={apiKey}
|
||||
to={getSettingsPath(SettingsPath.ApiKeyDetail, {
|
||||
apiKeyId: apiKey.id,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</StyledTableBodyContainer>
|
||||
)}
|
||||
</Table>
|
||||
);
|
||||
|
||||
+22
-25
@@ -11,23 +11,12 @@ import { useContext } from 'react';
|
||||
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { type Webhook } from '~/generated-metadata/graphql';
|
||||
|
||||
export const StyledApisFieldTableRow = styled(TableRow)`
|
||||
grid-template-columns: 1fr 28px;
|
||||
`;
|
||||
const WEBHOOK_TABLE_ROW_GRID_TEMPLATE_COLUMNS = '1fr 28px';
|
||||
|
||||
const StyledIconTableCell = styled(TableCell)`
|
||||
justify-content: center;
|
||||
padding-right: ${themeCssVariables.spacing[1]};
|
||||
padding-left: 0;
|
||||
`;
|
||||
|
||||
const StyledUrlTableCell = styled(TableCell)`
|
||||
color: ${themeCssVariables.font.color.primary};
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
const StyledIconChevronRight = styled(IconChevronRight)`
|
||||
const StyledIconChevronRightContainer = styled.span`
|
||||
align-items: center;
|
||||
color: ${themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const SettingsDevelopersWebhookTableRow = ({
|
||||
@@ -42,8 +31,11 @@ export const SettingsDevelopersWebhookTableRow = ({
|
||||
}) => {
|
||||
const { theme } = useContext(ThemeContext);
|
||||
return (
|
||||
<StyledApisFieldTableRow to={to}>
|
||||
<StyledUrlTableCell>
|
||||
<TableRow
|
||||
gridTemplateColumns={WEBHOOK_TABLE_ROW_GRID_TEMPLATE_COLUMNS}
|
||||
to={to}
|
||||
>
|
||||
<TableCell color={themeCssVariables.font.color.primary} overflow="hidden">
|
||||
<OverflowingTextWithTooltip
|
||||
text={
|
||||
isValidUrl(webhook.targetUrl)
|
||||
@@ -51,13 +43,18 @@ export const SettingsDevelopersWebhookTableRow = ({
|
||||
: webhook.targetUrl
|
||||
}
|
||||
/>
|
||||
</StyledUrlTableCell>
|
||||
<StyledIconTableCell>
|
||||
<StyledIconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
</StyledIconTableCell>
|
||||
</StyledApisFieldTableRow>
|
||||
</TableCell>
|
||||
<TableCell
|
||||
align="center"
|
||||
padding={`0 ${themeCssVariables.spacing[1]} 0 0`}
|
||||
>
|
||||
<StyledIconChevronRightContainer>
|
||||
<IconChevronRight
|
||||
size={theme.icon.size.md}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
/>
|
||||
</StyledIconChevronRightContainer>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
};
|
||||
|
||||
+16
-18
@@ -10,16 +10,12 @@ import { getSettingsPath } from 'twenty-shared/utils';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
import { useGetWebhooksQuery } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledTableBody = styled(TableBody)`
|
||||
const StyledTableBodyContainer = styled.div`
|
||||
border-bottom: 1px solid ${themeCssVariables.border.color.light};
|
||||
max-height: 260px;
|
||||
overflow-y: auto;
|
||||
`;
|
||||
|
||||
const StyledTableRow = styled(TableRow)`
|
||||
grid-template-columns: 444px 68px;
|
||||
`;
|
||||
|
||||
export const SettingsWebhooksTable = () => {
|
||||
const { data: webhooksData } = useGetWebhooksQuery();
|
||||
|
||||
@@ -27,22 +23,24 @@ export const SettingsWebhooksTable = () => {
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<StyledTableRow>
|
||||
<TableRow gridTemplateColumns="444px 68px">
|
||||
<TableHeader>URL</TableHeader>
|
||||
<TableHeader></TableHeader>
|
||||
</StyledTableRow>
|
||||
</TableRow>
|
||||
{!!webhooks?.length && (
|
||||
<StyledTableBody>
|
||||
{webhooks.map((webhookFieldItem) => (
|
||||
<SettingsDevelopersWebhookTableRow
|
||||
key={webhookFieldItem.id}
|
||||
webhook={webhookFieldItem}
|
||||
to={getSettingsPath(SettingsPath.WebhookDetail, {
|
||||
webhookId: webhookFieldItem.id,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
</StyledTableBody>
|
||||
<StyledTableBodyContainer>
|
||||
<TableBody>
|
||||
{webhooks.map((webhookFieldItem) => (
|
||||
<SettingsDevelopersWebhookTableRow
|
||||
key={webhookFieldItem.id}
|
||||
webhook={webhookFieldItem}
|
||||
to={getSettingsPath(SettingsPath.WebhookDetail, {
|
||||
webhookId: webhookFieldItem.id,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</StyledTableBodyContainer>
|
||||
)}
|
||||
</Table>
|
||||
);
|
||||
|
||||
+6
-5
@@ -60,13 +60,15 @@ const StyledControlLabel = styled.span`
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const StyledControlIconChevronDown = styled(IconChevronDown)<{
|
||||
const StyledControlIconChevronDownContainer = styled.span<{
|
||||
disabled?: boolean;
|
||||
}>`
|
||||
align-items: center;
|
||||
color: ${({ disabled }) =>
|
||||
disabled
|
||||
? themeCssVariables.font.color.extraLight
|
||||
: themeCssVariables.font.color.tertiary};
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
type WebhookEntitySelectProps = {
|
||||
@@ -166,10 +168,9 @@ export const WebhookEntitySelect = ({
|
||||
clickableComponent={
|
||||
<StyledControlContainer disabled={disabled}>
|
||||
<StyledControlLabel>{getSelectedLabel()}</StyledControlLabel>
|
||||
<StyledControlIconChevronDown
|
||||
disabled={disabled}
|
||||
size={theme.icon.size.md}
|
||||
/>
|
||||
<StyledControlIconChevronDownContainer disabled={disabled}>
|
||||
<IconChevronDown size={theme.icon.size.md} />
|
||||
</StyledControlIconChevronDownContainer>
|
||||
</StyledControlContainer>
|
||||
}
|
||||
dropdownComponents={
|
||||
|
||||
Reference in New Issue
Block a user