mirror of
https://github.com/strapi/strapi.git
synced 2025-09-04 14:23:03 +00:00
Merge remote-tracking branch 'origin/features/custom-fields' into custom-fields/attributes-list
This commit is contained in:
commit
c97ebd192a
@ -42,7 +42,10 @@ const AttributeOption = ({ type }) => {
|
|||||||
|
|
||||||
<Flex>
|
<Flex>
|
||||||
<Typography variant="pi" textColor="neutral600">
|
<Typography variant="pi" textColor="neutral600">
|
||||||
{formatMessage({ id: getTrad(`attribute.${type}.description`) })}
|
{formatMessage({
|
||||||
|
id: getTrad(`attribute.${type}.description`),
|
||||||
|
defaultMessage: 'A type for modeling data',
|
||||||
|
})}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -12,58 +12,82 @@ import { Divider } from '@strapi/design-system/Divider';
|
|||||||
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||||
import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable';
|
import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable';
|
||||||
import { ModalBody } from '@strapi/design-system/ModalLayout';
|
import { ModalBody } from '@strapi/design-system/ModalLayout';
|
||||||
import { Flex } from '@strapi/design-system/Flex';
|
|
||||||
import { Stack } from '@strapi/design-system/Stack';
|
import { Stack } from '@strapi/design-system/Stack';
|
||||||
|
import { Flex } from '@strapi/design-system/Flex';
|
||||||
import { Typography } from '@strapi/design-system/Typography';
|
import { Typography } from '@strapi/design-system/Typography';
|
||||||
|
import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs';
|
||||||
import { getTrad } from '../../utils';
|
import { getTrad } from '../../utils';
|
||||||
import AttributeOption from './AttributeOption';
|
import AttributeOption from './AttributeOption';
|
||||||
|
|
||||||
const AttributeOptions = ({ attributes, forTarget, kind }) => {
|
const AttributeOptions = ({ attributes, forTarget, kind }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
const defaultTabId = getTrad('modalForm.tabs.default');
|
||||||
|
const customTabId = getTrad('modalForm.tabs.custom');
|
||||||
|
|
||||||
const titleIdSuffix = forTarget.includes('component') ? 'component' : kind;
|
const titleIdSuffix = forTarget.includes('component') ? 'component' : kind;
|
||||||
const titleId = getTrad(`modalForm.sub-header.chooseAttribute.${titleIdSuffix}`);
|
const titleId = getTrad(`modalForm.sub-header.chooseAttribute.${titleIdSuffix}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalBody>
|
<ModalBody padding={6}>
|
||||||
<Flex paddingBottom={4}>
|
<TabGroup
|
||||||
<Typography variant="beta" as="h2">
|
label={formatMessage({
|
||||||
{formatMessage({ id: titleId, defaultMessage: 'Select a field' })}
|
id: 'modalForm.tab-group.label',
|
||||||
</Typography>
|
defaultMessage: 'Default and Custom types tabs',
|
||||||
</Flex>
|
})}
|
||||||
<Divider />
|
id="attribute-type-tabs"
|
||||||
<Box paddingTop={6} paddingBottom={4}>
|
variant="simple"
|
||||||
<KeyboardNavigable tagName="button">
|
>
|
||||||
<Stack spacing={8}>
|
<Flex justifyContent="space-between">
|
||||||
{attributes.map((attributeRow, index) => {
|
<Typography variant="beta" as="h2">
|
||||||
const key = index;
|
{formatMessage({ id: titleId, defaultMessage: 'Select a field' })}
|
||||||
|
</Typography>
|
||||||
|
<Tabs>
|
||||||
|
<Tab>{formatMessage({ id: defaultTabId, defaultMessage: 'Default' })}</Tab>
|
||||||
|
<Tab>{formatMessage({ id: customTabId, defaultMessage: 'Custom' })}</Tab>
|
||||||
|
</Tabs>
|
||||||
|
</Flex>
|
||||||
|
<Box paddingBottom={6}>
|
||||||
|
<Divider />
|
||||||
|
</Box>
|
||||||
|
<TabPanels>
|
||||||
|
<TabPanel>
|
||||||
|
<KeyboardNavigable tagName="button">
|
||||||
|
<Stack spacing={8}>
|
||||||
|
{attributes.map((attributeRow, index) => {
|
||||||
|
const key = index;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid key={key} gap={0}>
|
<Grid key={key} gap={0}>
|
||||||
{attributeRow.map((attribute, index) => {
|
{attributeRow.map((attribute, index) => {
|
||||||
const isOdd = index % 2 === 1;
|
const isOdd = index % 2 === 1;
|
||||||
const paddingLeft = isOdd ? 2 : 0;
|
const paddingLeft = isOdd ? 2 : 0;
|
||||||
const paddingRight = isOdd ? 0 : 2;
|
const paddingRight = isOdd ? 0 : 2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GridItem key={attribute} col={6} style={{ height: '100%' }}>
|
<GridItem key={attribute} col={6} style={{ height: '100%' }}>
|
||||||
<Box
|
<Box
|
||||||
paddingLeft={paddingLeft}
|
paddingLeft={paddingLeft}
|
||||||
paddingRight={paddingRight}
|
paddingRight={paddingRight}
|
||||||
paddingBottom={1}
|
paddingBottom={1}
|
||||||
style={{ height: '100%' }}
|
style={{ height: '100%' }}
|
||||||
>
|
>
|
||||||
<AttributeOption type={attribute} />
|
<AttributeOption type={attribute} />
|
||||||
</Box>
|
</Box>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Stack>
|
</Stack>
|
||||||
</KeyboardNavigable>
|
</KeyboardNavigable>
|
||||||
</Box>
|
</TabPanel>
|
||||||
|
<TabPanel>
|
||||||
|
<Typography>Coming soon</Typography>
|
||||||
|
</TabPanel>
|
||||||
|
</TabPanels>
|
||||||
|
</TabGroup>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,77 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, screen, getByText, fireEvent } from '@testing-library/react';
|
||||||
|
import { lightTheme, ThemeProvider } from '@strapi/design-system';
|
||||||
|
import { IntlProvider } from 'react-intl';
|
||||||
|
import FormModalNavigationProvider from '../../FormModalNavigationProvider';
|
||||||
|
import AttributeOptions from '../index';
|
||||||
|
|
||||||
|
const mockAttributes = [
|
||||||
|
[
|
||||||
|
'text',
|
||||||
|
'email',
|
||||||
|
'richtext',
|
||||||
|
'password',
|
||||||
|
'number',
|
||||||
|
'enumeration',
|
||||||
|
'date',
|
||||||
|
'media',
|
||||||
|
'boolean',
|
||||||
|
'json',
|
||||||
|
'relation',
|
||||||
|
'uid',
|
||||||
|
],
|
||||||
|
['component', 'dynamiczone'],
|
||||||
|
];
|
||||||
|
|
||||||
|
const App = (
|
||||||
|
<IntlProvider locale="en" messages={{}} textComponent="span">
|
||||||
|
<ThemeProvider theme={lightTheme}>
|
||||||
|
<FormModalNavigationProvider>
|
||||||
|
<AttributeOptions
|
||||||
|
attributes={mockAttributes}
|
||||||
|
forTarget="contentType"
|
||||||
|
kind="collectionType"
|
||||||
|
/>
|
||||||
|
</FormModalNavigationProvider>
|
||||||
|
</ThemeProvider>
|
||||||
|
</IntlProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('<AttributeOptions />', () => {
|
||||||
|
it('renders and matches the snapshot', () => {
|
||||||
|
const { container } = render(App);
|
||||||
|
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows the simple tabs', () => {
|
||||||
|
render(App);
|
||||||
|
|
||||||
|
const defaultTab = screen.getByRole('tab', { selected: true });
|
||||||
|
const customTab = screen.getByRole('tab', { selected: false });
|
||||||
|
|
||||||
|
expect(defaultTab).toBeVisible();
|
||||||
|
expect(customTab).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('defaults to the default tab', () => {
|
||||||
|
render(App);
|
||||||
|
|
||||||
|
const comingSoonText = screen.queryByText('Coming soon');
|
||||||
|
|
||||||
|
expect(comingSoonText).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('switches to the custom tab', () => {
|
||||||
|
render(App);
|
||||||
|
|
||||||
|
const customTab = screen.getByRole('tab', { selected: false });
|
||||||
|
fireEvent.click(customTab);
|
||||||
|
const customTabSelected = screen.getByRole('tab', { selected: true });
|
||||||
|
const customTabText = getByText(customTabSelected, 'Custom');
|
||||||
|
const comingSoonText = screen.getByText('Coming soon');
|
||||||
|
|
||||||
|
expect(customTabText).not.toBe(null);
|
||||||
|
expect(comingSoonText).toBeVisible();
|
||||||
|
});
|
||||||
|
});
|
@ -155,6 +155,9 @@
|
|||||||
"modalForm.sub-header.chooseAttribute.collectionType": "Select a field for your collection type",
|
"modalForm.sub-header.chooseAttribute.collectionType": "Select a field for your collection type",
|
||||||
"modalForm.sub-header.chooseAttribute.component": "Select a field for your component",
|
"modalForm.sub-header.chooseAttribute.component": "Select a field for your component",
|
||||||
"modalForm.sub-header.chooseAttribute.singleType": "Select a field for your single type",
|
"modalForm.sub-header.chooseAttribute.singleType": "Select a field for your single type",
|
||||||
|
"modalForm.tabs.custom": "Custom",
|
||||||
|
"modalForm.tabs.default": "Default",
|
||||||
|
"modalForm.tabs.label": "Default and Custom types tabs",
|
||||||
"modelPage.attribute.relation-polymorphic": "Relation (polymorphic)",
|
"modelPage.attribute.relation-polymorphic": "Relation (polymorphic)",
|
||||||
"modelPage.attribute.relationWith": "Relation with",
|
"modelPage.attribute.relationWith": "Relation with",
|
||||||
"notification.error.dynamiczone-min.validation": "At least one component is required in a dynamic zone to be able to save a content type",
|
"notification.error.dynamiczone-min.validation": "At least one component is required in a dynamic zone to be able to save a content type",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user