Merge remote-tracking branch 'origin/features/custom-fields' into custom-fields/attributes-list

This commit is contained in:
Rémi de Juvigny 2022-07-20 10:16:27 +02:00
commit c97ebd192a
5 changed files with 1410 additions and 39 deletions

View File

@ -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>

View File

@ -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>
); );
}; };

View File

@ -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();
});
});

View File

@ -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",