Skip to content

Commit

Permalink
Merge pull request #255 from VirtusLab-Open-Source/develop
Browse files Browse the repository at this point in the history
Version 2.2.0 preparations
  • Loading branch information
cyp3rius authored Jul 28, 2022
2 parents 7bd1cc2 + d026575 commit 2eb9d42
Show file tree
Hide file tree
Showing 63 changed files with 2,367 additions and 1,312 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v14.19.1
v14.20.0
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,25 +155,38 @@ Config for this plugin is stored as a part of the `config/plugins.js` or `config
navigation: {
enabled: true,
config: {
additionalFields: ['audience'],
additionalFields: ['audience', { name: 'my_custom_field', type: 'boolean', label: 'My custom field' }],
contentTypes: ['api::page.page'],
contentTypesNameFields: {
'api::page.page': ['title']
},
allowedLevels: 2,
gql: {...},
slugify: {
customReplacements: [
["🤔", "thinking"],
],
}
}
}
});
```

### Properties
- `additionalFields` - Additional fields: 'audience', more in the future
- `additionalFields` - Additional fields for navigation items. More **[ here ](#additional-fields)**
- `allowedLevels` - Maximum level for which you're able to mark item as "Menu attached"
- `contentTypes` - UIDs of related content types
- `contentTypesNameFields` - Definition of content type title fields like `'api::<collection name>.<content type name>': ['field_name_1', 'field_name_2']`, if not set titles are pulled from fields like `['title', 'subject', 'name']`. **TIP** - Proper content type uid you can find in the URL of Content Manager where you're managing relevant entities like: `admin/content-manager/collectionType/< THE UID HERE >?page=1&pageSize=10&sort=Title:ASC&plugins[i18n][locale]=en`
- `gql` - If you're using GraphQL that's the right place to put all necessary settings. More **[ here ](#gql-configuration)**
- `i18nEnabled` - should you want to manage multi-locale content via navigation set this value `Enabled`. More **[ here ](#i18n-internationalization)**
- `slugify` - allows to extend configuration of our "slugging" solution of choice. To learn more visit the [documentation](https://github.com/sindresorhus/slugify#api). It can be left unset since it's optional. **This option can only be handled by configuration in config file**.

### Properties

### Additional Fields
It is advised to configure additional fields through the plugin's Settings Page. There you can find the table of custom fields and toggle input for the audience field. When enabled, the audience field can be customized through the content manager. Custom fields can be added, edited, toggled, and removed with the use of the table provided on the Settings Page. When removing custom fields be advised that their values in navigation items will be lost. Disabling the custom fields will not affect the data and can be done with no consequence of loosing information.

Creating configuration for additional fields with the `config.js` file should be done with caution. Config object contains the `additionalFields` property of type `Array<CustomField | 'audience'>`, where CustomField is of type `{type: 'string' | 'boolean', name: string, label: string}`. When creating custom fields be advised that the `name` property has to be unique. When editing a custom field it is advised not to edit its `name` and `type` properties. After config has been restored the custom fields that are not present in `config.js` file will be deleted and their values in navigation items will be lost.

## 🔧 GQL Configuration
Using navigation with GraphQL requires both plugins to be installed and working. You can find installation guide for GraphQL plugin **[here](https://docs.strapi.io/developer-docs/latest/plugins/graphql.html#graphql)**. To properly configure GQL to work with navigation you should provide `gql` prop. This should contain union types that will be used to define GQL response format for your data while fetching:
Expand Down Expand Up @@ -579,11 +592,8 @@ query {
"title": "Another page",
"path": "/another",
"related": {
"id": 2,
"attributes": {
"__typename": "Page",
"Title": "dfdfdf"
}
"__typename": "Page",
"Title": "Eg. Page title"
},
"items": []
}
Expand Down
9 changes: 7 additions & 2 deletions __mocks__/strapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const itemModelMock = {
navigationItemId: 56,
},
parent: null,
additionalFields: { string_test_field: 'Custom field value' },
}),
findMany: async (params: StrapiDBQueryArgs) => [{
id: 1,
Expand All @@ -72,6 +73,7 @@ const itemModelMock = {
navigationItemId: 56,
},
parent: null,
additionalFields: { string_test_field: 'Custom field value' },
}, {
id: 2,
title: "side",
Expand Down Expand Up @@ -107,6 +109,7 @@ const itemModelMock = {
createdAt: "2021-12-31T10:04:54.812Z",
updatedAt: "2022-01-14T13:36:29.430Z",
},
additionalFields: { string_test_field: 'Custom field value' },
}].filter(item => params.where ? isMatch(item, params.where) : true),
};

Expand Down Expand Up @@ -143,9 +146,10 @@ const plugins = (strapi: IStrapi): StringMap<StrapiPlugin> => ({
config: (key: string) => ({
...defaultConfig.default,
contentTypes: ['api::pages.pages'],
additionalFields: [{ name: 'string_test_field', label: "Test field", type: 'string', enabled: true }]
})[key],
get controllers() { return {} },
controller() { return () => {} },
controller() { return () => {} },
}
});

Expand Down Expand Up @@ -193,7 +197,8 @@ const strapiFactory = (plugins: (strapi: IStrapi) => StringMap<StrapiPlugin>, co
return {
get: ({ key }: { key: string }) => key === 'config' ? {
...(defaultConfig.default),
contentTypes: ['api::pages.pages']
contentTypes: ['api::pages.pages'],
additionalFields: [{ name: 'string_test_field', label: "Test field", type: 'string', enabled: true }]
} : null,
set: () => null,
}
Expand Down
90 changes: 90 additions & 0 deletions admin/src/components/AdditionalFieldInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import React, { BaseSyntheticEvent, useMemo } from 'react';
import { assertBoolean, assertString } from '../../../../types';
//@ts-ignore
import { ToggleInput } from '@strapi/design-system/ToggleInput';
//@ts-ignore
import { TextInput } from '@strapi/design-system/TextInput';
//@ts-ignore
import { Select, Option } from '@strapi/design-system/Select';
//@ts-ignore
import { useNotification } from '@strapi/helper-plugin';
import { getTrad } from '../../translations';
import { AdditionalFieldInputProps, Input } from './types';
import { isNil } from 'lodash';
import { useIntl } from 'react-intl';

const DEFAULT_STRING_VALUE = "";
const handlerFactory =
({ field, prop, onChange }: Input) =>
({ target }: BaseSyntheticEvent) => {
onChange(field.name, target[prop]);
};

const AdditionalFieldInput: React.FC<AdditionalFieldInputProps> = ({
field,
isLoading,
onChange,
value,
error
}) => {
const toggleNotification = useNotification();
const { formatMessage } = useIntl();
const defaultInputProps = useMemo(() => ({
id: field.name,
name: field.name,
label: field.label,
disabled: isLoading,
error: error && formatMessage(error),
}), [field, isLoading, error]);
const handleBoolean = useMemo(() => handlerFactory({ field, onChange, prop: "checked" }), [onChange, field]);
const handleString = useMemo(() => handlerFactory({ field, onChange, prop: "value" }), [onChange, field]);

switch (field.type) {
case 'boolean':
if (!isNil(value))
assertBoolean(value);
return (
<ToggleInput
{...defaultInputProps}
checked={!!value}
onChange={handleBoolean}
onLabel="true"
offLabel="false"
/>
);
case 'string':
if (!isNil(value))
assertString(value);
return (
<TextInput
{...defaultInputProps}
onChange={handleString}
value={value || DEFAULT_STRING_VALUE}
/>
);
case 'select':
return (
<Select
{...defaultInputProps}
onChange={(v: string) => onChange(field.name, v)}
value={isNil(value) ? field.multi ? [] : null : value}
multi={field.multi}
withTags={field.multi}
>
{field.options.map((option, index) => (
<Option key={`${field.name}-option-${index}`} value={option}>
{option}
</Option>
))}
</Select>
);
default:
toggleNotification({
type: 'warning',
message: getTrad('notification.error.customField.type'),
});
throw new Error(`Type of custom field is unsupported`);
}
}

export default AdditionalFieldInput;
14 changes: 14 additions & 0 deletions admin/src/components/AdditionalFieldInput/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { MessageDescriptor } from "react-intl";
import { NavigationItemCustomField } from "../../../../types";

export type AdditionalFieldInputProps = {
field: NavigationItemCustomField;
isLoading: boolean;
onChange: (name: string, value: string) => void;
value: string | boolean | string[] | null;
error: MessageDescriptor | null;
}
export type TargetProp = "value" | "checked";
export type Input = {
prop: TargetProp;
} & Pick<AdditionalFieldInputProps, "onChange" | "field">;
3 changes: 1 addition & 2 deletions admin/src/components/Item/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import { TextButton } from '@strapi/design-system/TextButton';
import { Typography } from '@strapi/design-system/Typography';
import { ArrowRight, Link as LinkIcon, Earth, Plus, Cog } from '@strapi/icons';

import { navigationItemType } from '../../pages/View/utils/enums';
import ItemCardHeader from './ItemCardHeader';
import List from '../NavigationItemList';
import Wrapper from './Wrapper';
import { extractRelatedItemLabel } from '../../pages/View/utils/parsers';
import ItemCardBadge from './ItemCardBadge';
import { ItemCardRemovedOverlay } from './ItemCardRemovedOverlay';
import { getMessage, ItemTypes } from '../../utils';
import { getMessage, ItemTypes, navigationItemType } from '../../utils';
import CollapseButton from '../CollapseButton';

const Item = (props) => {
Expand Down
39 changes: 39 additions & 0 deletions admin/src/components/TextArrayInput/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from 'react';
import { Effect } from '../../../../types';
// @ts-ignore
import { TextInput } from '@strapi/design-system/TextInput';
import { isArray } from 'lodash';

interface IProps {
onChange: Effect<string[]>;
initialValue?: string[];
id?: string;
name?: string;
label?: string;
disabled?: boolean;
error?: string | string[];
}

const TextArrayInput: React.FC<IProps> = ({ onChange, initialValue, ...props }) => {
const [value, setValue] = useState(isArray(initialValue)
? initialValue.reduce((acc, cur) => `${acc}${cur}; `, "")
: "");
const handleOnChange = ({target: { value }}: React.BaseSyntheticEvent) => {
const newValue: string = value;
const valuesArray = newValue
.split(';')
.map(v => v.trim())
.filter(v => !!v.length);
setValue(value);
onChange(valuesArray);
}
return (
<TextInput
{...props}
onChange={handleOnChange}
value={value}
/>
)
}

export default TextArrayInput;
13 changes: 0 additions & 13 deletions admin/src/hooks/useAllContentTypes.js

This file was deleted.

11 changes: 11 additions & 0 deletions admin/src/hooks/useAllContentTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//@ts-ignore
import { useQuery } from 'react-query';
import { pick } from 'lodash';
import { fetchAllContentTypes } from '../utils';

const useAllContentTypes = () => pick(
useQuery('contentTypes', () => fetchAllContentTypes()),
["data", "isLoading", "error"]
);

export default useAllContentTypes;
6 changes: 3 additions & 3 deletions admin/src/hooks/useNavigationConfig.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { useQuery, useQueryClient } from 'react-query';
import { useNotification } from '@strapi/helper-plugin';
import { fetchNavigationConfig, restartStrapi, restoreNavigationConfig, updateNavigationConfig } from '../utils/api';
import { fetchNavigationConfig, restartStrapi, restoreNavigationConfig, updateNavigationConfig } from '../utils';
import { getTrad } from '../translations';

const useNavigationConfig = () => {
const queryClient = useQueryClient();
const toggleNotification = useNotification();
const { isLoading, data, err } = useQuery('navigationConfig', () =>
const { isLoading, data, error } = useQuery('navigationConfig', () =>
fetchNavigationConfig(toggleNotification)
);

Expand Down Expand Up @@ -52,7 +52,7 @@ const useNavigationConfig = () => {
}
}

return { data, isLoading, err, submitMutation, restoreMutation, restartMutation };
return { data, isLoading, error, submitMutation, restoreMutation, restartMutation };
};

export default useNavigationConfig;
12 changes: 6 additions & 6 deletions admin/src/pages/DataManagerProvider/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
useAppInfos,
} from "@strapi/helper-plugin";
import DataManagerContext from "../../contexts/DataManagerContext";
import getTrad from "../../utils/getTrad";
import pluginId from "../../pluginId";
import init from "./init";
import { getTrad } from "../../translations";
import reducer, { initialState } from "./reducer";
import {
GET_NAVIGATION_DATA,
Expand Down Expand Up @@ -108,7 +108,7 @@ const DataManagerProvider = ({ children }) => {
console.error({ err });
toggleNotification({
type: 'warning',
message: { id: getTrad('notification.error') },
message: getTrad('notification.error'),
});
}
};
Expand Down Expand Up @@ -147,7 +147,7 @@ const DataManagerProvider = ({ children }) => {
console.error({ err });
toggleNotification({
type: 'warning',
message: { id: getTrad('notification.error') },
message: getTrad('notification.error'),
});
}
};
Expand All @@ -169,7 +169,7 @@ const DataManagerProvider = ({ children }) => {
if (!autoReload) {
toggleNotification({
type: 'info',
message: { id: getTrad('notification.info.autoreaload-disable') },
message: { id: 'notification.info.autoreaload-disable' },
});
}
}, [autoReload]);
Expand Down Expand Up @@ -310,7 +310,7 @@ const DataManagerProvider = ({ children }) => {
});
toggleNotification({
type: 'success',
message: { id: getTrad('notification.navigation.submit') },
message: getTrad('notification.navigation.submit'),
});
} catch (err) {
dispatch({
Expand All @@ -332,7 +332,7 @@ const DataManagerProvider = ({ children }) => {
}
toggleNotification({
type: 'warning',
message: { id: getTrad('notification.error') },
message: getTrad('notification.error'),
});
}
};
Expand Down
Loading

0 comments on commit 2eb9d42

Please sign in to comment.