mirror of
https://github.com/strapi/strapi.git
synced 2025-12-12 15:32:42 +00:00
feat(content-manager): add container queries to form fields (#22693)
This commit is contained in:
parent
c2c6a58d5a
commit
926e9af936
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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)`
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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' })
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user