mirror of
https://github.com/strapi/strapi.git
synced 2025-11-11 15:49:50 +00:00
Merge pull request #13820 from strapi/custom-fields/list-custom-fields
[Custom fields] list custom fields
This commit is contained in:
commit
e222cb29da
@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const ColorPickerInput = () => {
|
||||||
|
return <div>TODO: Map Input Component</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ColorPickerInput;
|
||||||
@ -4,7 +4,25 @@ import ColorPickerIcon from './components/ColorPicker/ColorPickerIcon';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
register(app) {
|
register(app) {
|
||||||
app.customFields.register({
|
app.customFields.register([
|
||||||
|
{
|
||||||
|
name: 'map',
|
||||||
|
pluginId: 'mycustomfields',
|
||||||
|
type: 'json',
|
||||||
|
intlLabel: {
|
||||||
|
id: 'mycustomfields.map.label',
|
||||||
|
defaultMessage: 'Map',
|
||||||
|
},
|
||||||
|
intlDescription: {
|
||||||
|
id: 'mycustomfields.map.description',
|
||||||
|
defaultMessage: 'Select any location',
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Input: async () =>
|
||||||
|
import(/* webpackChunkName: "input-component" */ './components/Map/MapInput'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
name: 'color',
|
name: 'color',
|
||||||
pluginId: 'mycustomfields',
|
pluginId: 'mycustomfields',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
@ -23,7 +41,8 @@ export default {
|
|||||||
/* webpackChunkName: "input-component" */ './components/ColorPicker/ColorPickerInput'
|
/* webpackChunkName: "input-component" */ './components/ColorPicker/ColorPickerInput'
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
]);
|
||||||
},
|
},
|
||||||
bootstrap(app) {},
|
bootstrap(app) {},
|
||||||
async registerTrads({ locales }) {
|
async registerTrads({ locales }) {
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Box } from '@strapi/design-system/Box';
|
||||||
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||||
|
import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable';
|
||||||
|
import { Stack } from '@strapi/design-system/Stack';
|
||||||
|
import AttributeOption from '../AttributeOption';
|
||||||
|
import getPadding from '../utils/getPadding';
|
||||||
|
|
||||||
|
const AttributeList = ({ attributes }) => (
|
||||||
|
<KeyboardNavigable tagName="button">
|
||||||
|
<Stack spacing={8}>
|
||||||
|
{attributes.map((attributeRow, index) => {
|
||||||
|
return (
|
||||||
|
// eslint-disable-next-line react/no-array-index-key
|
||||||
|
<Grid key={index} gap={0}>
|
||||||
|
{attributeRow.map((attribute, index) => {
|
||||||
|
const { paddingLeft, paddingRight } = getPadding(index);
|
||||||
|
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
|
||||||
|
AttributeList.propTypes = {
|
||||||
|
attributes: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AttributeList;
|
||||||
@ -13,7 +13,7 @@ import { Typography } from '@strapi/design-system/Typography';
|
|||||||
import useFormModalNavigation from '../../../hooks/useFormModalNavigation';
|
import useFormModalNavigation from '../../../hooks/useFormModalNavigation';
|
||||||
import getTrad from '../../../utils/getTrad';
|
import getTrad from '../../../utils/getTrad';
|
||||||
import AttributeIcon from '../../AttributeIcon';
|
import AttributeIcon from '../../AttributeIcon';
|
||||||
import BoxWrapper from './BoxWrapper';
|
import OptionBoxWrapper from '../OptionBoxWrapper';
|
||||||
|
|
||||||
const AttributeOption = ({ type }) => {
|
const AttributeOption = ({ type }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@ -30,7 +30,7 @@ const AttributeOption = ({ type }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BoxWrapper padding={4} as="button" hasRadius type="button" onClick={handleClick}>
|
<OptionBoxWrapper padding={4} as="button" hasRadius type="button" onClick={handleClick}>
|
||||||
<Flex>
|
<Flex>
|
||||||
<AttributeIcon type={type} />
|
<AttributeIcon type={type} />
|
||||||
<Box paddingLeft={4}>
|
<Box paddingLeft={4}>
|
||||||
@ -50,7 +50,7 @@ const AttributeOption = ({ type }) => {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
</BoxWrapper>
|
</OptionBoxWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* AttributeOption
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import { Box } from '@strapi/design-system/Box';
|
||||||
|
import { Flex } from '@strapi/design-system/Flex';
|
||||||
|
import { Typography } from '@strapi/design-system/Typography';
|
||||||
|
import OptionBoxWrapper from '../OptionBoxWrapper';
|
||||||
|
import AttributeIcon from '../../AttributeIcon';
|
||||||
|
import useFormModalNavigation from '../../../hooks/useFormModalNavigation';
|
||||||
|
|
||||||
|
const CustomFieldOption = ({ customFieldUid, customField }) => {
|
||||||
|
const { type, intlLabel, intlDescription } = customField;
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
const { onClickSelectCustomField } = useFormModalNavigation();
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
onClickSelectCustomField({
|
||||||
|
attributeType: type,
|
||||||
|
customFieldUid,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<OptionBoxWrapper padding={4} as="button" hasRadius type="button" onClick={handleClick}>
|
||||||
|
<Flex>
|
||||||
|
<AttributeIcon type={type} customField={customFieldUid} />
|
||||||
|
<Box paddingLeft={4}>
|
||||||
|
<Flex>
|
||||||
|
<Typography fontWeight="bold">{formatMessage(intlLabel)}</Typography>
|
||||||
|
</Flex>
|
||||||
|
<Flex>
|
||||||
|
<Typography variant="pi" textColor="neutral600">
|
||||||
|
{formatMessage(intlDescription)}
|
||||||
|
</Typography>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
</OptionBoxWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
CustomFieldOption.propTypes = {
|
||||||
|
customFieldUid: PropTypes.string.isRequired,
|
||||||
|
customField: PropTypes.shape({
|
||||||
|
type: PropTypes.string.isRequired,
|
||||||
|
icon: PropTypes.func,
|
||||||
|
intlLabel: PropTypes.shape({
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
defaultMessage: PropTypes.string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
intlDescription: PropTypes.shape({
|
||||||
|
id: PropTypes.string.isRequired,
|
||||||
|
defaultMessage: PropTypes.string.isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
}).isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomFieldOption;
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useCustomFields } from '@strapi/helper-plugin';
|
||||||
|
import { Box } from '@strapi/design-system/Box';
|
||||||
|
import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||||
|
import { KeyboardNavigable } from '@strapi/design-system/KeyboardNavigable';
|
||||||
|
import { Stack } from '@strapi/design-system/Stack';
|
||||||
|
import { Link } from '@strapi/design-system/Link';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import EmptyAttributes from '../EmptyAttributes';
|
||||||
|
import CustomFieldOption from '../CustomFieldOption';
|
||||||
|
import getPadding from '../utils/getPadding';
|
||||||
|
import { getTrad } from '../../../utils';
|
||||||
|
|
||||||
|
const CustomFieldsList = () => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
const customFields = useCustomFields();
|
||||||
|
const registeredCustomFields = Object.entries(customFields.getAll());
|
||||||
|
|
||||||
|
if (!registeredCustomFields.length) return <EmptyAttributes />;
|
||||||
|
|
||||||
|
// Sort the array alphabetically by customField name
|
||||||
|
const sortedCustomFields = registeredCustomFields.sort((a, b) =>
|
||||||
|
a[1].name > b[1].name ? 1 : -1
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<KeyboardNavigable tagName="button">
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<Grid gap={0}>
|
||||||
|
{sortedCustomFields.map(([uid, customField], index) => {
|
||||||
|
const { paddingLeft, paddingRight } = getPadding(index);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GridItem key={uid} col={6} style={{ height: '100%' }}>
|
||||||
|
<Box
|
||||||
|
paddingLeft={paddingLeft}
|
||||||
|
paddingRight={paddingRight}
|
||||||
|
paddingBottom={1}
|
||||||
|
style={{ height: '100%' }}
|
||||||
|
>
|
||||||
|
<CustomFieldOption key={uid} customFieldUid={uid} customField={customField} />
|
||||||
|
</Box>
|
||||||
|
</GridItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
<Link
|
||||||
|
href="https://docs.strapi.io/developer-docs/latest/getting-started/introduction.html"
|
||||||
|
isExternal
|
||||||
|
>
|
||||||
|
{formatMessage({
|
||||||
|
id: getTrad('modalForm.tabs.custom.howToLink'),
|
||||||
|
defaultMessage: 'How to add custom fields',
|
||||||
|
})}
|
||||||
|
</Link>
|
||||||
|
</Stack>
|
||||||
|
</KeyboardNavigable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomFieldsList;
|
||||||
@ -9,16 +9,13 @@ import { useIntl } from 'react-intl';
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Box } from '@strapi/design-system/Box';
|
import { Box } from '@strapi/design-system/Box';
|
||||||
import { Divider } from '@strapi/design-system/Divider';
|
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 { ModalBody } from '@strapi/design-system/ModalLayout';
|
||||||
import { Stack } from '@strapi/design-system/Stack';
|
|
||||||
import { Flex } from '@strapi/design-system/Flex';
|
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 { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/design-system/Tabs';
|
||||||
import { getTrad } from '../../utils';
|
import { getTrad } from '../../utils';
|
||||||
import AttributeOption from './AttributeOption';
|
import AttributeList from './AttributeList';
|
||||||
import EmptyAttributes from './EmptyAttributes';
|
import CustomFieldsList from './CustomFieldsList';
|
||||||
|
|
||||||
const AttributeOptions = ({ attributes, forTarget, kind }) => {
|
const AttributeOptions = ({ attributes, forTarget, kind }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@ -53,39 +50,10 @@ const AttributeOptions = ({ attributes, forTarget, kind }) => {
|
|||||||
</Box>
|
</Box>
|
||||||
<TabPanels>
|
<TabPanels>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<KeyboardNavigable tagName="button">
|
<AttributeList attributes={attributes} />
|
||||||
<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 (
|
|
||||||
<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>
|
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<EmptyAttributes />
|
<CustomFieldsList />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</TabGroup>
|
</TabGroup>
|
||||||
|
|||||||
@ -123,17 +123,6 @@ exports[`<AttributeOptions /> renders and matches the snapshot 1`] = `
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.c15 {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(12,1fr);
|
|
||||||
gap: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c16 {
|
|
||||||
grid-column: span 6;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.c0 {
|
.c0 {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
}
|
}
|
||||||
@ -175,6 +164,17 @@ exports[`<AttributeOptions /> renders and matches the snapshot 1`] = `
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.c15 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(12,1fr);
|
||||||
|
gap: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c16 {
|
||||||
|
grid-column: span 6;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.c21 {
|
.c21 {
|
||||||
width: 2rem;
|
width: 2rem;
|
||||||
height: 1.5rem;
|
height: 1.5rem;
|
||||||
|
|||||||
@ -1,12 +1,41 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Router } from 'react-router-dom';
|
import { Router } from 'react-router-dom';
|
||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
import { render, screen, getByText, fireEvent } from '@testing-library/react';
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
import { lightTheme, ThemeProvider } from '@strapi/design-system';
|
import { lightTheme, ThemeProvider } from '@strapi/design-system';
|
||||||
import { IntlProvider } from 'react-intl';
|
import { IntlProvider } from 'react-intl';
|
||||||
import FormModalNavigationProvider from '../../FormModalNavigationProvider';
|
import FormModalNavigationProvider from '../../FormModalNavigationProvider';
|
||||||
import AttributeOptions from '../index';
|
import AttributeOptions from '../index';
|
||||||
|
|
||||||
|
const mockCustomField = {
|
||||||
|
'plugin::mycustomfields.test': {
|
||||||
|
name: 'color',
|
||||||
|
pluginId: 'mycustomfields',
|
||||||
|
type: 'text',
|
||||||
|
icon: jest.fn(),
|
||||||
|
intlLabel: {
|
||||||
|
id: 'mycustomfields.color.label',
|
||||||
|
defaultMessage: 'Color',
|
||||||
|
},
|
||||||
|
intlDescription: {
|
||||||
|
id: 'mycustomfields.color.description',
|
||||||
|
defaultMessage: 'Select any color',
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Input: jest.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAll = jest.fn().mockReturnValue({});
|
||||||
|
jest.mock('@strapi/helper-plugin', () => ({
|
||||||
|
...jest.requireActual('@strapi/helper-plugin'),
|
||||||
|
useCustomFields: () => ({
|
||||||
|
get: jest.fn().mockReturnValue(mockCustomField),
|
||||||
|
getAll,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
const mockAttributes = [
|
const mockAttributes = [
|
||||||
[
|
[
|
||||||
'text',
|
'text',
|
||||||
@ -57,8 +86,8 @@ describe('<AttributeOptions />', () => {
|
|||||||
const App = makeApp();
|
const App = makeApp();
|
||||||
render(App);
|
render(App);
|
||||||
|
|
||||||
const defaultTab = screen.getByRole('tab', { selected: true });
|
const defaultTab = screen.getByRole('tab', { selected: true, name: 'Default' });
|
||||||
const customTab = screen.getByRole('tab', { selected: false });
|
const customTab = screen.getByRole('tab', { selected: false, name: 'Custom' });
|
||||||
|
|
||||||
expect(defaultTab).toBeVisible();
|
expect(defaultTab).toBeVisible();
|
||||||
expect(customTab).toBeVisible();
|
expect(customTab).toBeVisible();
|
||||||
@ -73,17 +102,34 @@ describe('<AttributeOptions />', () => {
|
|||||||
expect(comingSoonText).toEqual(null);
|
expect(comingSoonText).toEqual(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('switches to the custom tab', () => {
|
it('switches to the custom tab without custom fields', () => {
|
||||||
const App = makeApp();
|
const App = makeApp();
|
||||||
render(App);
|
render(App);
|
||||||
|
|
||||||
const customTab = screen.getByRole('tab', { selected: false });
|
getAll.mockReturnValueOnce({});
|
||||||
|
|
||||||
|
const customTab = screen.getByRole('tab', { selected: false, name: 'Custom' });
|
||||||
fireEvent.click(customTab);
|
fireEvent.click(customTab);
|
||||||
const customTabSelected = screen.getByRole('tab', { selected: true });
|
const customTabSelected = screen.getByRole('tab', { selected: true, name: 'Custom' });
|
||||||
const customTabText = getByText(customTabSelected, 'Custom');
|
|
||||||
const comingSoonText = screen.getByText('Nothing in here yet.');
|
const comingSoonText = screen.getByText('Nothing in here yet.');
|
||||||
|
|
||||||
expect(customTabText).not.toBe(null);
|
expect(customTabSelected).toBeVisible();
|
||||||
expect(comingSoonText).toBeVisible();
|
expect(comingSoonText).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('switches to the custom tab with custom fields', () => {
|
||||||
|
getAll.mockReturnValue(mockCustomField);
|
||||||
|
const App = makeApp();
|
||||||
|
render(App);
|
||||||
|
|
||||||
|
const customTab = screen.getByRole('tab', { selected: false, name: 'Custom' });
|
||||||
|
fireEvent.click(customTab);
|
||||||
|
const customTabSelected = screen.getByRole('tab', { selected: true, name: 'Custom' });
|
||||||
|
const customFieldText = screen.getByText('Color');
|
||||||
|
const howToAddLink = screen.getByRole('link', { name: 'How to add custom fields' });
|
||||||
|
|
||||||
|
expect(customTabSelected).toBeVisible();
|
||||||
|
expect(customFieldText).toBeVisible();
|
||||||
|
expect(howToAddLink).toBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
const getPadding = index => {
|
||||||
|
const isOdd = index % 2 === 1;
|
||||||
|
const paddingLeft = isOdd ? 2 : 0;
|
||||||
|
const paddingRight = isOdd ? 0 : 2;
|
||||||
|
|
||||||
|
return { paddingLeft, paddingRight };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getPadding;
|
||||||
@ -10,6 +10,7 @@ const INITIAL_STATE_DATA = {
|
|||||||
kind: null,
|
kind: null,
|
||||||
step: null,
|
step: null,
|
||||||
targetUid: null,
|
targetUid: null,
|
||||||
|
customFieldUid: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { INITIAL_STATE_DATA };
|
export { INITIAL_STATE_DATA };
|
||||||
|
|||||||
@ -8,6 +8,20 @@ const FormModalNavigationProvider = ({ children }) => {
|
|||||||
const [state, setFormModalNavigationState] = useState(INITIAL_STATE_DATA);
|
const [state, setFormModalNavigationState] = useState(INITIAL_STATE_DATA);
|
||||||
const { trackUsage } = useTracking();
|
const { trackUsage } = useTracking();
|
||||||
|
|
||||||
|
const onClickSelectCustomField = ({ attributeType, customFieldUid }) => {
|
||||||
|
// TODO: Add tracking for custom fields
|
||||||
|
setFormModalNavigationState(prevState => {
|
||||||
|
return {
|
||||||
|
...prevState,
|
||||||
|
actionType: 'create',
|
||||||
|
// TODO: Create a new modalType on EXPANSION-245
|
||||||
|
modalType: 'attribute',
|
||||||
|
attributeType,
|
||||||
|
customFieldUid,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onClickSelectField = ({ attributeType, step }) => {
|
const onClickSelectField = ({ attributeType, step }) => {
|
||||||
if (state.forTarget === 'contentType') {
|
if (state.forTarget === 'contentType') {
|
||||||
trackUsage('didSelectContentTypeFieldType', { type: attributeType });
|
trackUsage('didSelectContentTypeFieldType', { type: attributeType });
|
||||||
@ -47,7 +61,6 @@ const FormModalNavigationProvider = ({ children }) => {
|
|||||||
forTarget,
|
forTarget,
|
||||||
targetUid,
|
targetUid,
|
||||||
modalType: 'chooseAttribute',
|
modalType: 'chooseAttribute',
|
||||||
|
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -146,6 +159,7 @@ const FormModalNavigationProvider = ({ children }) => {
|
|||||||
value={{
|
value={{
|
||||||
...state,
|
...state,
|
||||||
onClickSelectField,
|
onClickSelectField,
|
||||||
|
onClickSelectCustomField,
|
||||||
onCloseModal,
|
onCloseModal,
|
||||||
onNavigateToChooseAttributeModal,
|
onNavigateToChooseAttributeModal,
|
||||||
onNavigateToAddCompoToDZModal,
|
onNavigateToAddCompoToDZModal,
|
||||||
|
|||||||
@ -0,0 +1,47 @@
|
|||||||
|
import { renderHook, act } from '@testing-library/react-hooks';
|
||||||
|
import { INITIAL_STATE_DATA } from '../constants';
|
||||||
|
import FormModalNavigationProvider from '../index';
|
||||||
|
import useFormModalNavigation from '../../../hooks/useFormModalNavigation';
|
||||||
|
|
||||||
|
const removeFunctionsFromObject = state => {
|
||||||
|
const stringified = JSON.stringify(state);
|
||||||
|
const parsed = JSON.parse(stringified);
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('FromModalNavigationProvider', () => {
|
||||||
|
it('sets the initial state', () => {
|
||||||
|
const { result } = renderHook(() => useFormModalNavigation(), {
|
||||||
|
wrapper: FormModalNavigationProvider,
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentStateWithoutFunctions = removeFunctionsFromObject(result.current);
|
||||||
|
|
||||||
|
expect(currentStateWithoutFunctions).toEqual(INITIAL_STATE_DATA);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates the form navigation state when selecting a custom field', () => {
|
||||||
|
const { result } = renderHook(() => useFormModalNavigation(), {
|
||||||
|
wrapper: FormModalNavigationProvider,
|
||||||
|
});
|
||||||
|
|
||||||
|
act(() => {
|
||||||
|
result.current.onClickSelectCustomField({
|
||||||
|
attributeType: 'text',
|
||||||
|
customFieldUid: 'plugin::mycustomfields.color',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentStateWithoutFunctions = removeFunctionsFromObject(result.current);
|
||||||
|
const expected = {
|
||||||
|
...INITIAL_STATE_DATA,
|
||||||
|
actionType: 'create',
|
||||||
|
modalType: 'attribute',
|
||||||
|
attributeType: 'text',
|
||||||
|
customFieldUid: 'plugin::mycustomfields.color',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(currentStateWithoutFunctions).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -49,7 +49,7 @@ DisplayedType.defaultProps = {
|
|||||||
|
|
||||||
DisplayedType.propTypes = {
|
DisplayedType.propTypes = {
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
customField: PropTypes.bool,
|
customField: PropTypes.string,
|
||||||
repeatable: PropTypes.bool,
|
repeatable: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -160,6 +160,7 @@
|
|||||||
"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.custom": "Custom",
|
||||||
|
"modalForm.tabs.custom.howToLink": "How to add custom fields",
|
||||||
"modalForm.tabs.default": "Default",
|
"modalForm.tabs.default": "Default",
|
||||||
"modalForm.tabs.label": "Default and Custom types tabs",
|
"modalForm.tabs.label": "Default and Custom types tabs",
|
||||||
"modelPage.attribute.relation-polymorphic": "Relation (polymorphic)",
|
"modelPage.attribute.relation-polymorphic": "Relation (polymorphic)",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user