mirror of
https://github.com/strapi/strapi.git
synced 2025-12-03 18:42:47 +00:00
Add tabs to attribute modal
This commit is contained in:
parent
4949da189e
commit
ded30b56e3
@ -12,66 +12,71 @@ import { Divider } from '@strapi/design-system/Divider';
|
||||
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||
import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable';
|
||||
import { ModalBody } from '@strapi/design-system/ModalLayout';
|
||||
import { Flex } from '@strapi/design-system/Flex';
|
||||
import { Stack } from '@strapi/design-system/Stack';
|
||||
import { Typography } from '@strapi/design-system/Typography';
|
||||
import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs';
|
||||
import { getTrad } from '../../utils';
|
||||
import AttributeOption from './AttributeOption';
|
||||
|
||||
const AttributeOptions = ({ attributes, forTarget, kind }) => {
|
||||
const AttributeOptions = ({ attributes }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const titleIdSuffix = forTarget.includes('component') ? 'component' : kind;
|
||||
const titleId = getTrad(`modalForm.sub-header.chooseAttribute.${titleIdSuffix}`);
|
||||
const defaultTabId = getTrad(`modalForm.tabs.default`);
|
||||
const customTabId = getTrad(`modalForm.tabs.custom`);
|
||||
|
||||
return (
|
||||
<ModalBody>
|
||||
<Flex paddingBottom={4}>
|
||||
<Typography variant="beta" as="h2">
|
||||
{formatMessage({ id: titleId, defaultMessage: 'Select a field' })}
|
||||
</Typography>
|
||||
</Flex>
|
||||
<Divider />
|
||||
<Box paddingTop={6} paddingBottom={4}>
|
||||
<KeyboardNavigable tagName="button">
|
||||
<Stack spacing={8}>
|
||||
{attributes.map((attributeRow, index) => {
|
||||
const key = index;
|
||||
<ModalBody paddingTop={3} paddingLeft={6} paddingRight={6} paddingBottom={4}>
|
||||
<TabGroup label="Attribute type tabs" id="attribute-type-tabs" variant="simple">
|
||||
<Tabs>
|
||||
<Tab>{formatMessage({ id: defaultTabId, defaultMessage: 'Default' })}</Tab>
|
||||
<Tab>{formatMessage({ id: customTabId, defaultMessage: 'Custom' })}</Tab>
|
||||
</Tabs>
|
||||
<Box paddingBottom={6}>
|
||||
<Divider />
|
||||
</Box>
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<KeyboardNavigable tagName="button">
|
||||
<Stack spacing={8}>
|
||||
{attributes.map((attributeRow, index) => {
|
||||
const key = index;
|
||||
|
||||
return (
|
||||
<Grid key={key} gap={0}>
|
||||
{attributeRow.map((attribute, index) => {
|
||||
const isOdd = index % 2 === 1;
|
||||
const paddingLeft = isOdd ? 2 : 0;
|
||||
const paddingRight = isOdd ? 0 : 2;
|
||||
return (
|
||||
<Grid key={key} gap={0}>
|
||||
{attributeRow.map((attribute, index) => {
|
||||
const isOdd = index % 2 === 1;
|
||||
const paddingLeft = isOdd ? 2 : 0;
|
||||
const paddingRight = isOdd ? 0 : 2;
|
||||
|
||||
return (
|
||||
<GridItem key={attribute} col={6} style={{ height: '100%' }}>
|
||||
<Box
|
||||
paddingLeft={paddingLeft}
|
||||
paddingRight={paddingRight}
|
||||
paddingBottom={1}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<AttributeOption type={attribute} />
|
||||
</Box>
|
||||
</GridItem>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</KeyboardNavigable>
|
||||
</Box>
|
||||
return (
|
||||
<GridItem key={attribute} col={6} style={{ height: '100%' }}>
|
||||
<Box
|
||||
paddingLeft={paddingLeft}
|
||||
paddingRight={paddingRight}
|
||||
paddingBottom={2}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<AttributeOption type={attribute} />
|
||||
</Box>
|
||||
</GridItem>
|
||||
);
|
||||
})}
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</KeyboardNavigable>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<Box>Coming soon</Box>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
</ModalBody>
|
||||
);
|
||||
};
|
||||
|
||||
AttributeOptions.propTypes = {
|
||||
attributes: PropTypes.array.isRequired,
|
||||
forTarget: PropTypes.string.isRequired,
|
||||
kind: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default AttributeOptions;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,105 @@
|
||||
import { render, screen, getByText, fireEvent } from '@testing-library/react';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import React from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { lightTheme, darkTheme } from '@strapi/design-system';
|
||||
import LanguageProvider from '../../../../../../admin/admin/src/components/LanguageProvider';
|
||||
import Theme from '../../../../../../admin/admin/src/components/Theme';
|
||||
import ThemeToggleProvider from '../../../../../../admin/admin/src/components/ThemeToggleProvider';
|
||||
import en from '../../../../../../admin/admin/src/translations/en.json';
|
||||
import FormModalNavigationProvider from '../../FormModalNavigationProvider';
|
||||
import pluginEn from '../../../translations/en.json';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import AttributeOptions from '../index';
|
||||
|
||||
const mockAttributes = [
|
||||
[
|
||||
'text',
|
||||
'email',
|
||||
'richtext',
|
||||
'password',
|
||||
'number',
|
||||
'enumeration',
|
||||
'date',
|
||||
'media',
|
||||
'boolean',
|
||||
'json',
|
||||
'relation',
|
||||
'uid',
|
||||
],
|
||||
['component', 'dynamiczone'],
|
||||
];
|
||||
|
||||
const makeApp = () => {
|
||||
const history = createMemoryHistory();
|
||||
const messages = {
|
||||
en: Object.keys(pluginEn).reduce(
|
||||
(acc, current) => {
|
||||
acc[getTrad(current)] = pluginEn[current];
|
||||
|
||||
return acc;
|
||||
},
|
||||
{ ...en }
|
||||
),
|
||||
};
|
||||
|
||||
const localeNames = { en: 'English' };
|
||||
|
||||
return (
|
||||
<LanguageProvider messages={messages} localeNames={localeNames}>
|
||||
<ThemeToggleProvider themes={{ light: lightTheme, dark: darkTheme }}>
|
||||
<Theme>
|
||||
<Router history={history}>
|
||||
<FormModalNavigationProvider>
|
||||
<AttributeOptions attributes={mockAttributes} />
|
||||
</FormModalNavigationProvider>
|
||||
</Router>
|
||||
</Theme>
|
||||
</ThemeToggleProvider>
|
||||
</LanguageProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('AttributeOptions', () => {
|
||||
it('renders and matches the snapshot', () => {
|
||||
const App = makeApp();
|
||||
const { container } = render(App);
|
||||
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('shows the simple tabs', async () => {
|
||||
const App = makeApp();
|
||||
render(App);
|
||||
|
||||
const tabs = screen.getByLabelText('Attribute type tabs');
|
||||
const defaultTab = await getByText(tabs, 'Default');
|
||||
const customTab = await getByText(tabs, 'Custom');
|
||||
|
||||
expect(defaultTab).toBeVisible();
|
||||
expect(customTab).toBeVisible();
|
||||
});
|
||||
|
||||
it('defaults to the default tab', async () => {
|
||||
const App = makeApp();
|
||||
render(App);
|
||||
|
||||
const comingSoonText = screen.queryByText('Coming soon');
|
||||
|
||||
expect(comingSoonText).toEqual(null);
|
||||
});
|
||||
|
||||
it('switches to the custom tab', async () => {
|
||||
const App = makeApp();
|
||||
render(App);
|
||||
|
||||
const customTab = screen.getByRole('tab', { selected: false });
|
||||
fireEvent.click(customTab);
|
||||
const button = screen.getByRole('tab', { selected: true });
|
||||
const customTabActive = await getByText(button, 'Custom');
|
||||
const comingSoonText = screen.getByText('Coming soon');
|
||||
|
||||
expect(customTabActive).not.toBe(null);
|
||||
expect(comingSoonText).toBeVisible();
|
||||
});
|
||||
});
|
||||
@ -891,8 +891,6 @@ const FormModal = () => {
|
||||
advancedFormInputNames.includes(key)
|
||||
);
|
||||
|
||||
const schemaKind = get(contentTypes, [targetUid, 'schema', 'kind']);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ModalLayout onClose={handleClosed} labelledBy="title">
|
||||
@ -907,13 +905,7 @@ const FormModal = () => {
|
||||
targetUid={targetUid}
|
||||
attributeType={attributeType}
|
||||
/>
|
||||
{isPickingAttribute && (
|
||||
<AttributeOptions
|
||||
attributes={displayedAttributes}
|
||||
forTarget={forTarget}
|
||||
kind={schemaKind || 'collectionType'}
|
||||
/>
|
||||
)}
|
||||
{isPickingAttribute && <AttributeOptions attributes={displayedAttributes} />}
|
||||
{!isPickingAttribute && (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<ModalBody>
|
||||
|
||||
@ -155,6 +155,8 @@
|
||||
"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.singleType": "Select a field for your single type",
|
||||
"modalForm.tabs.default": "Default",
|
||||
"modalForm.tabs.custom": "Custom",
|
||||
"modelPage.attribute.relation-polymorphic": "Relation (polymorphic)",
|
||||
"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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user