feat(content-manager): add container queries to form fields (#22693)

This commit is contained in:
markkaylor 2025-01-22 16:59:44 +01:00 committed by GitHub
parent c2c6a58d5a
commit 926e9af936
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 52 additions and 21 deletions

View File

@ -1,7 +1,8 @@
import { useField } from '@strapi/admin/strapi-admin';
import { Box, Flex, Grid } from '@strapi/design-system';
import { Box, Flex } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { ResponsiveGridItem, ResponsiveGridRoot } from '../../FormLayout';
import { ComponentProvider, useComponent } from '../ComponentContext';
import type { ComponentInputProps } from './Input';
@ -33,7 +34,7 @@ const NonRepeatableComponent = ({
<Flex direction="column" alignItems="stretch" gap={6}>
{layout.map((row, index) => {
return (
<Grid.Root gap={4} key={index}>
<ResponsiveGridRoot gap={4} key={index}>
{row.map(({ size, ...field }) => {
/**
* Layouts are built from schemas so they don't understand the complete
@ -49,7 +50,7 @@ const NonRepeatableComponent = ({
});
return (
<Grid.Item
<ResponsiveGridItem
col={size}
key={completeFieldName}
s={12}
@ -58,10 +59,10 @@ const NonRepeatableComponent = ({
alignItems="stretch"
>
{children({ ...field, label: translatedLabel, name: completeFieldName })}
</Grid.Item>
</ResponsiveGridItem>
);
})}
</Grid.Root>
</ResponsiveGridRoot>
);
})}
</Flex>

View File

@ -9,7 +9,6 @@ import {
Accordion,
IconButton,
useComposedRefs,
Grid,
BoxComponent,
} from '@strapi/design-system';
import { Plus, Drag, Trash } from '@strapi/icons';
@ -26,6 +25,7 @@ import { getIn } from '../../../../../utils/objects';
import { getTranslation } from '../../../../../utils/translations';
import { transformDocument } from '../../../utils/data';
import { createDefaultForm } from '../../../utils/forms';
import { ResponsiveGridItem, ResponsiveGridRoot } from '../../FormLayout';
import { ComponentProvider, useComponent } from '../ComponentContext';
import { Initializer } from './Initializer';
@ -273,7 +273,7 @@ const RepeatableComponent = ({
>
{layout.map((row, index) => {
return (
<Grid.Root gap={4} key={index}>
<ResponsiveGridRoot gap={4} key={index}>
{row.map(({ size, ...field }) => {
/**
* Layouts are built from schemas so they don't understand the complete
@ -289,7 +289,7 @@ const RepeatableComponent = ({
});
return (
<Grid.Item
<ResponsiveGridItem
col={size}
key={completeFieldName}
s={12}
@ -302,10 +302,10 @@ const RepeatableComponent = ({
label: translatedLabel,
name: completeFieldName,
})}
</Grid.Item>
</ResponsiveGridItem>
);
})}
</Grid.Root>
</ResponsiveGridRoot>
);
})}
</Component>

View File

@ -5,6 +5,7 @@ import { useIntl } from 'react-intl';
import { styled } from 'styled-components';
import { ComponentIcon } from '../../../../../components/ComponentIcon';
import { RESPONSIVE_CONTAINER_BREAKPOINTS } from '../../FormLayout';
interface ComponentCategoryProps {
category: string;
@ -34,7 +35,7 @@ const ComponentCategory = ({
{formatMessage({ id: category, defaultMessage: category })}
</Accordion.Trigger>
</Accordion.Header>
<Accordion.Content>
<ResponsiveAccordionContent>
<Grid paddingTop={4} paddingBottom={4} paddingLeft={3} paddingRight={3}>
{components.map(({ uid, displayName, icon }) => (
<ComponentBox
@ -59,15 +60,23 @@ const ComponentCategory = ({
</ComponentBox>
))}
</Grid>
</Accordion.Content>
</ResponsiveAccordionContent>
</Accordion.Item>
);
};
const ResponsiveAccordionContent = styled(Accordion.Content)`
container-type: inline-size;
`;
const Grid = styled(Box)`
display: grid;
grid-template-columns: repeat(auto-fit, 14rem);
grid-template-columns: repeat(auto-fill, 100%);
grid-gap: ${({ theme }) => theme.spaces[1]};
@container (min-width: ${() => RESPONSIVE_CONTAINER_BREAKPOINTS.sm}) {
grid-template-columns: repeat(auto-fill, 14rem);
}
`;
const ComponentBox = styled<FlexComponent<'button'>>(Flex)`

View File

@ -23,6 +23,7 @@ import { useDocLayout } from '../../../../../hooks/useDocumentLayout';
import { type UseDragAndDropOptions, useDragAndDrop } from '../../../../../hooks/useDragAndDrop';
import { getIn } from '../../../../../utils/objects';
import { getTranslation } from '../../../../../utils/translations';
import { ResponsiveGridItem, ResponsiveGridRoot } from '../../FormLayout';
import { InputRenderer, type InputRendererProps } from '../../InputRenderer';
import type { ComponentPickerProps } from './ComponentPicker';
@ -244,7 +245,7 @@ const DynamicComponent = ({
direction="column"
alignItems="stretch"
>
<Grid.Root gap={4}>
<ResponsiveGridRoot gap={4}>
{row.map(({ size, ...field }) => {
const fieldName = `${name}.${index}.${field.name}`;
@ -257,7 +258,7 @@ const DynamicComponent = ({
};
return (
<Grid.Item
<ResponsiveGridItem
col={size}
key={fieldName}
s={12}
@ -270,10 +271,10 @@ const DynamicComponent = ({
) : (
<InputRenderer {...fieldWithTranslatedLabel} name={fieldName} />
)}
</Grid.Item>
</ResponsiveGridItem>
);
})}
</Grid.Root>
</ResponsiveGridRoot>
</Grid.Item>
))}
</Grid.Root>

View File

@ -1,11 +1,28 @@
import { Box, Flex, Grid } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import { styled } from 'styled-components';
import { useDoc } from '../../../hooks/useDocument';
import { EditLayout } from '../../../hooks/useDocumentLayout';
import { InputRenderer } from './InputRenderer';
export const RESPONSIVE_CONTAINER_BREAKPOINTS = {
sm: '27.5rem', // 440px
};
export const ResponsiveGridRoot = styled(Grid.Root)`
container-type: inline-size;
`;
export const ResponsiveGridItem = styled(Grid.Item)`
grid-column: span 12;
@container (min-width: ${RESPONSIVE_CONTAINER_BREAKPOINTS.sm}) {
${({ col }) => col && `grid-column: span ${col};`}
}
`;
interface FormLayoutProps extends Pick<EditLayout, 'layout'> {}
const FormLayout = ({ layout }: FormLayoutProps) => {
@ -50,7 +67,7 @@ const FormLayout = ({ layout }: FormLayoutProps) => {
>
<Flex direction="column" alignItems="stretch" gap={6}>
{panel.map((row, gridRowIndex) => (
<Grid.Root key={gridRowIndex} gap={4}>
<ResponsiveGridRoot key={gridRowIndex} gap={4}>
{row.map(({ size, ...field }) => {
const fieldWithTranslatedLabel = {
...field,
@ -60,7 +77,7 @@ const FormLayout = ({ layout }: FormLayoutProps) => {
}),
};
return (
<Grid.Item
<ResponsiveGridItem
col={size}
key={field.name}
s={12}
@ -69,10 +86,10 @@ const FormLayout = ({ layout }: FormLayoutProps) => {
alignItems="stretch"
>
<InputRenderer {...fieldWithTranslatedLabel} />
</Grid.Item>
</ResponsiveGridItem>
);
})}
</Grid.Root>
</ResponsiveGridRoot>
))}
</Flex>
</Box>

View File

@ -110,8 +110,11 @@ test.describe('Settings', () => {
* Create the missing components in the "content" dynamic zone.
*/
await page.getByRole('button', { name: 'There are 2 missing components' }).click();
// Click the button in the DZ component picker
await page.getByRole('button', { name: 'Product carousel' }).click();
// Click the DZ component toggle button
await page.getByRole('button', { name: 'Product carousel' }).click();
await page
.getByRole('region', { name: /Product carousel/ })
.getByRole('textbox', { name: 'title' })