strapi-plugin-navigation 2.0.0-beta.1 → 2.0.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +62 -104
  2. package/admin/src/components/Item/ItemCardHeader/index.js +2 -2
  3. package/admin/src/components/Item/index.js +3 -1
  4. package/admin/src/components/NavigationItemList/index.js +2 -0
  5. package/admin/src/components/Search/index.js +49 -0
  6. package/admin/src/pages/DataManagerProvider/index.js +6 -9
  7. package/admin/src/pages/DataManagerProvider/reducer.js +58 -69
  8. package/admin/src/pages/View/index.js +16 -4
  9. package/admin/src/pages/View/utils/parsers.js +1 -1
  10. package/admin/src/pluginId.js +2 -2
  11. package/package.json +7 -3
  12. package/server/config/index.js +8 -0
  13. package/server/controllers/navigation.js +21 -0
  14. package/server/graphql/index.js +23 -0
  15. package/server/graphql/queries/index.js +17 -0
  16. package/server/graphql/queries/render-navigation-child.js +16 -0
  17. package/server/graphql/queries/render-navigation.js +15 -0
  18. package/server/graphql/resolvers-config.js +4 -0
  19. package/server/graphql/types/content-types-name-fields.js +8 -0
  20. package/server/graphql/types/content-types.js +16 -0
  21. package/server/graphql/types/create-navigation-item.js +17 -0
  22. package/server/graphql/types/create-navigation-related.js +8 -0
  23. package/server/graphql/types/create-navigation.js +7 -0
  24. package/server/graphql/types/index.js +15 -0
  25. package/server/graphql/types/navigation-config.js +9 -0
  26. package/server/graphql/types/navigation-details.js +10 -0
  27. package/server/graphql/types/navigation-item.js +29 -0
  28. package/server/graphql/types/navigation-related.js +23 -0
  29. package/server/graphql/types/navigation-render-type.js +4 -0
  30. package/server/graphql/types/navigation.js +9 -0
  31. package/server/register.js +5 -0
  32. package/server/routes/client.js +21 -0
  33. package/server/routes/index.js +2 -1
  34. package/server/services/navigation.js +272 -6
  35. package/server/services/utils/functions.js +84 -2
  36. package/strapi-server.js +3 -1
@@ -14,10 +14,8 @@ import { ContentLayout } from '@strapi/design-system/Layout';
14
14
  import { Button } from '@strapi/design-system/Button';
15
15
  import { LoadingIndicatorPage } from "@strapi/helper-plugin";
16
16
  import { EmptyStateLayout } from '@strapi/design-system/EmptyStateLayout';
17
- import { IconButton } from '@strapi/design-system/IconButton';
18
17
  import EmptyDocumentsIcon from '@strapi/icons/EmptyDocuments';
19
18
  import PlusIcon from "@strapi/icons/Plus";
20
- import SearchIcon from "@strapi/icons/Search";
21
19
 
22
20
  // Components
23
21
  import List from '../../components/NavigationItemList';
@@ -32,6 +30,7 @@ import {
32
30
  usedContentTypes,
33
31
  validateNavigationStructure,
34
32
  } from './utils/parsers';
33
+ import Search from '../../components/Search';
35
34
 
36
35
  const View = () => {
37
36
  const {
@@ -55,6 +54,9 @@ const View = () => {
55
54
  const [activeNavigationItem, setActiveNavigationItemState] = useState({});
56
55
  const { formatMessage } = useIntl();
57
56
 
57
+ const [searchValue, setSearchValue] = useState('');
58
+ const isSearchEmpty = isEmpty(searchValue);
59
+
58
60
  const structureHasErrors = !validateNavigationStructure((changedActiveNavigation || {}).items);
59
61
  const navigationSelectValue = get(activeNavigation, "id", null);
60
62
  const handleSave = () => isLoadingForSubmit || structureHasErrors
@@ -104,6 +106,15 @@ const View = () => {
104
106
  handleChangeNavigationData(changedStructure, true);
105
107
  };
106
108
 
109
+ const filteredListFactory = (items, filterFunction) => items.reduce((acc, item) => {
110
+ const subItems = !isEmpty(item.items) ? filteredListFactory(item.items, filterFunction) : [];
111
+ if (filterFunction(item))
112
+ return [item, ...subItems, ...acc];
113
+ else
114
+ return [...subItems, ...acc];
115
+ }, []);
116
+ const filteredList = !isSearchEmpty ? filteredListFactory(changedActiveNavigation.items, (item) => item?.title.includes(searchValue)) : [];
117
+
107
118
  const handleItemRemove = (item) => {
108
119
  handleSubmitNavigationItem({
109
120
  ...item,
@@ -147,7 +158,7 @@ const View = () => {
147
158
  {changedActiveNavigation && (
148
159
  <>
149
160
  <NavigationContentHeader
150
- startActions={<IconButton icon={<SearchIcon />} />}
161
+ startActions={<Search value={searchValue} setValue={setSearchValue}/>}
151
162
  endActions={<Button
152
163
  onClick={addNewNavigationItem}
153
164
  startIcon={<PlusIcon />}
@@ -176,11 +187,12 @@ const View = () => {
176
187
  {
177
188
  !isEmpty(changedActiveNavigation.items || [])
178
189
  && <List
179
- items={changedActiveNavigation.items || []}
190
+ items={isSearchEmpty ? changedActiveNavigation.items || [] : filteredList}
180
191
  onItemLevelAdd={addNewNavigationItem}
181
192
  onItemRemove={handleItemRemove}
182
193
  onItemEdit={handleItemEdit}
183
194
  onItemRestore={handleItemRestore}
195
+ displayFlat={!isSearchEmpty}
184
196
  root
185
197
  error={error}
186
198
  allowedLevels={config.allowedLevels}
@@ -28,7 +28,7 @@ export const transformItemToRESTPayload = (
28
28
  const { contentTypes = [] } = config;
29
29
 
30
30
  const parsedRelated = Number(related);
31
- const relatedId = isExternal || isNaN(parsedRelated) ? related.value || related : parsedRelated;
31
+ const relatedId = isExternal || isNaN(parsedRelated) ? related?.value || related : parsedRelated;
32
32
 
33
33
  const relatedContentType = relatedType ?
34
34
  find(contentTypes,
@@ -1,5 +1,5 @@
1
- import pluginPkg from '../../package.json';
1
+ const pluginPkg = require('../../package.json');
2
2
 
3
3
  const pluginId = pluginPkg.name.replace(/^strapi-plugin-/i, '');
4
4
 
5
- export default pluginId;
5
+ module.exports = pluginId;
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "strapi-plugin-navigation",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-beta.5",
4
4
  "description": "Strapi - Navigation plugin",
5
5
  "strapi": {
6
- "name": "Navigation",
6
+ "name": "navigation",
7
7
  "icon": "hamburger",
8
8
  "description": "UI navigation management",
9
9
  "kind": "plugin"
@@ -16,7 +16,8 @@
16
16
  "test:unit": "jest --verbose --coverage"
17
17
  },
18
18
  "dependencies": {
19
- "@types/uuid": "^8.3.0",
19
+ "@strapi/utils": "^4.0.5",
20
+ "uuid": "^8.3.0",
20
21
  "bad-words": "^3.0.3",
21
22
  "lodash": "^4.17.11",
22
23
  "react": "^16.9.0",
@@ -38,6 +39,9 @@
38
39
  "jest-styled-components": "^7.0.2",
39
40
  "codecov": "^3.7.2"
40
41
  },
42
+ "peerDependencies": {
43
+ "@strapi/strapi": "4.x"
44
+ },
41
45
  "author": {
42
46
  "name": "VirtusLab // Mateusz Ziarko",
43
47
  "email": "mziarko@virtuslab.com",
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ default: {
3
+ additionalFields: [],
4
+ contentTypes: [],
5
+ contentTypesNameFields: {},
6
+ allowedLevels: 2
7
+ }
8
+ }
@@ -48,4 +48,25 @@ module.exports = {
48
48
  return getService().put(id, body, auditLog)
49
49
  .catch(errorHandler(ctx));
50
50
  },
51
+ async render(ctx) {
52
+ const { params, query = {} } = ctx;
53
+ const { type, menu: menuOnly } = query;
54
+ const { idOrSlug } = parseParams(params);
55
+ return getService().render(
56
+ idOrSlug,
57
+ type,
58
+ menuOnly,
59
+ );
60
+ },
61
+ async renderChild(ctx) {
62
+ const { params, query = {} } = ctx;
63
+ const { type, menu: menuOnly } = query;
64
+ const { idOrSlug, childUIKey } = parseParams(params);
65
+ return getService().renderChildren(
66
+ idOrSlug,
67
+ childUIKey,
68
+ type,
69
+ menuOnly
70
+ );
71
+ },
51
72
  };
@@ -0,0 +1,23 @@
1
+ const getTypes = require('./types');
2
+ const getQueries = require('./queries');
3
+ const getResolversConfig = require('./resolvers-config');
4
+
5
+ module.exports = () => {
6
+ const extensionService = strapi.plugin('graphql').service('extension');
7
+
8
+ extensionService.shadowCRUD('plugin::navigation.audience').disable();
9
+ extensionService.shadowCRUD('plugin::navigation.navigation').disable();
10
+ extensionService.shadowCRUD('plugin::navigation.navigation-item').disable();
11
+ extensionService.shadowCRUD('plugin::navigation.navigations-items-related').disable();
12
+
13
+ extensionService.use(({ nexus }) => {
14
+ const types = getTypes({ strapi, nexus });
15
+ const queries = getQueries({ strapi, nexus });
16
+ const resolversConfig = getResolversConfig({ strapi });
17
+
18
+ return {
19
+ types: [types, queries],
20
+ resolversConfig,
21
+ }
22
+ });
23
+ }
@@ -0,0 +1,17 @@
1
+ module.exports = (context) => {
2
+ const queries = {
3
+ renderNavigationChild: require('./render-navigation-child'),
4
+ renderNavigation: require('./render-navigation'),
5
+ }
6
+
7
+ return context.nexus.extendType({
8
+ type: 'Query',
9
+ definition(t) {
10
+ for (const [name, configFactory] of Object.entries(queries)) {
11
+ const config = configFactory(context);
12
+
13
+ t.field(name, config);
14
+ }
15
+ },
16
+ });
17
+ };
@@ -0,0 +1,16 @@
1
+ module.exports = ({ strapi, nexus }) => {
2
+ const { nonNull, list, stringArg, booleanArg } = nexus;
3
+ return {
4
+ type: nonNull(list('NavigationItem')),
5
+ args: {
6
+ id: nonNull(stringArg()),
7
+ childUiKey: nonNull(stringArg()),
8
+ type: 'NavigationRenderType',
9
+ menuOnly: booleanArg()
10
+ },
11
+ resolve(obj, args) {
12
+ const { id, childUIKey, type, menuOnly } = args;
13
+ return strapi.plugin('navigation').service('navigation').renderChildren(id, childUIKey, type, menuOnly);
14
+ },
15
+ };
16
+ }
@@ -0,0 +1,15 @@
1
+ module.exports = ({ strapi, nexus }) => {
2
+ const { nonNull, list, stringArg, booleanArg } = nexus;
3
+ return {
4
+ type: nonNull(list('NavigationItem')),
5
+ args: {
6
+ navigationIdOrSlug: nonNull(stringArg()),
7
+ type: 'NavigationRenderType',
8
+ menuOnly: booleanArg()
9
+ },
10
+ resolve(obj, args) {
11
+ const { navigationIdOrSlug, type, menuOnly } = args;
12
+ return strapi.plugin('navigation').service('navigation').render(navigationIdOrSlug, type, menuOnly);
13
+ },
14
+ };
15
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = ({ }) => ({
2
+ 'Query.renderNavigationChild': { auth: false },
3
+ 'Query.renderNavigation': { auth: false },
4
+ });
@@ -0,0 +1,8 @@
1
+ module.exports = ({ nexus }) => nexus.objectType({
2
+ name: "ContentTypesNameFields",
3
+ definition(t) {
4
+ t.nonNull.list.nonNull.string("default")
5
+ const contentTypesNameFields = strapi.plugin('navigation').config('contentTypesNameFields')
6
+ Object.keys(contentTypesNameFields || {}).forEach(key => t.nonNull.list.string(key))
7
+ }
8
+ })
@@ -0,0 +1,16 @@
1
+ module.exports = ({ nexus }) => nexus.objectType({
2
+ name: "ContentTypes",
3
+ definition(t) {
4
+ t.nonNull.string("uid")
5
+ t.nonNull.string("name")
6
+ t.nonNull.boolean("isSingle")
7
+ t.nonNull.string("collectionName")
8
+ t.nonNull.string("contentTypeName")
9
+ t.nonNull.string("label")
10
+ t.nonNull.string("relatedField")
11
+ t.nonNull.string("labelSingular")
12
+ t.nonNull.string("endpoint")
13
+ t.nonNull.boolean("available")
14
+ t.nonNull.boolean("visible")
15
+ }
16
+ })
@@ -0,0 +1,17 @@
1
+ module.exports = ({ nexus }) => nexus.inputObjectType({
2
+ name: "CreateNavigationItem",
3
+ definition(t) {
4
+ t.nonNull.string("title")
5
+ t.nonNull.string("type")
6
+ t.string("path")
7
+ t.string("externalPath")
8
+ t.nonNull.string("uiRouterKey")
9
+ t.nonNull.boolean("menuAttached")
10
+ t.nonNull.int("order")
11
+ t.int("parent")
12
+ t.int("master")
13
+ t.list.field("items", { type: 'CreateNavigationItem' })
14
+ t.list.string("audience")
15
+ t.field("related", { type: 'CreateNavigationRelated' })
16
+ }
17
+ });
@@ -0,0 +1,8 @@
1
+ module.exports = ({ nexus }) => nexus.inputObjectType({
2
+ name: "CreateNavigationRelated",
3
+ definition(t) {
4
+ t.nonNull.string("ref")
5
+ t.nonNull.string("field")
6
+ t.nonNull.string("refId")
7
+ }
8
+ });
@@ -0,0 +1,7 @@
1
+ module.exports = ({ nexus }) => nexus.inputObjectType({
2
+ name: "CreateNavigation",
3
+ definition(t) {
4
+ t.nonNull.string("name")
5
+ t.nonNull.list.field("items", { type: 'CreateNavigationItem' })
6
+ }
7
+ });
@@ -0,0 +1,15 @@
1
+ const typesFactories = [
2
+ require('./navigation-item'),
3
+ require('./navigation-related'),
4
+ require('./navigation-render-type'),
5
+ require('./navigation'),
6
+ require('./navigation-details'),
7
+ require('./content-types-name-fields'),
8
+ require('./content-types'),
9
+ require('./navigation-config'),
10
+ require('./create-navigation-related'),
11
+ require('./create-navigation-item'),
12
+ require('./create-navigation'),
13
+ ];
14
+
15
+ module.exports = context => typesFactories.map(factory => factory(context));
@@ -0,0 +1,9 @@
1
+ module.exports = ({ nexus }) => nexus.objectType({
2
+ name: "NavigationConfig",
3
+ definition(t) {
4
+ t.int("allowedLevels");
5
+ t.nonNull.list.string("additionalFields");
6
+ t.field("contentTypesNameFields", { type: 'ContentTypesNameFields' });
7
+ t.list.field("contentTypes", { type: 'ContentTypes' });
8
+ }
9
+ })
@@ -0,0 +1,10 @@
1
+ module.exports = ({ nexus }) => nexus.objectType({
2
+ name: "NavigationDetails",
3
+ definition(t) {
4
+ t.nonNull.string("id")
5
+ t.nonNull.string("name")
6
+ t.nonNull.string("slug")
7
+ t.nonNull.boolean("visible")
8
+ t.nonNull.list.field("items", { type: 'NavigationItem' })
9
+ }
10
+ })
@@ -0,0 +1,29 @@
1
+ module.exports = ({ nexus }) =>
2
+ nexus.objectType({
3
+ name: "NavigationItem",
4
+ definition(t) {
5
+ t.nonNull.int("id")
6
+ t.nonNull.string("title")
7
+ t.nonNull.string("type")
8
+ t.string("path")
9
+ t.string("externalPath")
10
+ t.nonNull.string("uiRouterKey")
11
+ t.nonNull.boolean("menuAttached")
12
+ t.nonNull.int("order")
13
+ t.int("parent")
14
+ t.int("master")
15
+ t.list.field("items", { type: 'NavigationItem' })
16
+ t.list.field("related", { type: 'NavigationRelated' })
17
+ t.list.string("audience")
18
+ // SQL
19
+ t.string("created_at")
20
+ t.string("updated_at")
21
+ t.string("created_by")
22
+ t.string("updated_by")
23
+ // MONGO
24
+ t.string("createdAt")
25
+ t.string("updatedAt")
26
+ t.string("createdBy")
27
+ t.string("updatedBy")
28
+ }
29
+ });
@@ -0,0 +1,23 @@
1
+ module.exports = ({ strapi, nexus }) => {
2
+ const related = strapi.plugin('navigation').config('gql').navigationItemRelated;
3
+ const name = "NavigationRelated";
4
+
5
+ if (related?.length) {
6
+ return nexus.unionType({
7
+ name,
8
+ definition(t) {
9
+ t.members(...related)
10
+ },
11
+ resolveType: (item) => strapi.contentTypes[item.__contentType]?.globalId
12
+ });
13
+ }
14
+
15
+ return nexus.objectType({
16
+ name,
17
+ definition(t) {
18
+ t.int("id")
19
+ t.string("title")
20
+ t.string("name")
21
+ }
22
+ })
23
+ }
@@ -0,0 +1,4 @@
1
+ module.exports = ({nexus}) => nexus.enumType({
2
+ name: "NavigationRenderType",
3
+ members: ['FLAT','TREE','RFR'],
4
+ });
@@ -0,0 +1,9 @@
1
+ module.exports = ({ nexus }) => nexus.objectType({
2
+ name: "Navigation",
3
+ definition(t) {
4
+ t.nonNull.string("id")
5
+ t.nonNull.string("name")
6
+ t.nonNull.string("slug")
7
+ t.nonNull.boolean("visible")
8
+ }
9
+ })
@@ -0,0 +1,5 @@
1
+ module.exports = ({ strapi }) => {
2
+ if (strapi.plugin('graphql')) {
3
+ require('./graphql')({ strapi });
4
+ }
5
+ };
@@ -0,0 +1,21 @@
1
+ module.exports = {
2
+ type: 'content-api',
3
+ routes: [
4
+ {
5
+ method: "GET",
6
+ path: "/render/:idOrSlug",
7
+ handler: "navigation.render",
8
+ config: {
9
+ policies: []
10
+ }
11
+ },
12
+ {
13
+ method: "GET",
14
+ path: "/render/:idOrSlug/:childUIKey",
15
+ handler: "navigation.renderChild",
16
+ config: {
17
+ policies: []
18
+ }
19
+ }
20
+ ]
21
+ }
@@ -1,3 +1,4 @@
1
1
  module.exports = {
2
- 'admin': require('./admin')
2
+ 'admin': require('./admin'),
3
+ 'content-api': require('./client'),
3
4
  };