Skip to content

Commit

Permalink
feat: ability to move resource on top in the navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
Wojciech Krysiak authored and Wojciech Krysiak committed Sep 17, 2020
1 parent 1befad5 commit 28e0a5d
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 55 deletions.
4 changes: 4 additions & 0 deletions example-app/src/companies/company.admin.js
Expand Up @@ -31,6 +31,10 @@ const options = {
isDisabled: true,
},
},
parent: {
name: null,
icon: 'Enterprise',
},
actions: {
search: {
handler: async (request, response, data) => {
Expand Down
18 changes: 12 additions & 6 deletions src/backend/decorators/resource-decorator.ts
Expand Up @@ -254,16 +254,22 @@ class ResourceDecorator {
* @return {Parent} ResourceJSON['parent']}
*/
getParent(): ResourceJSON['parent'] {
const DEFAULT_ICON = 'Archive'
// when user gives parent: null
if (this.options.parent === null) {
return null
}
const parent = (
this.options.parent || this._resource.databaseName()
) as {name: string; icon: string}
const name = (parent.name || parent) as string
const icon = parent.icon ? parent.icon : `icon-${this._resource.databaseType() || 'database'}`
return { name, icon }
if (this.options.parent === undefined || typeof this.options.parent === 'string') {
return {
name: this.options.parent || this._resource.databaseName(),
icon: this._resource.databaseType() || DEFAULT_ICON,
}
}
const { name, icon } = this.options.parent
return {
name: name || null,
icon: icon || DEFAULT_ICON,
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/backend/decorators/resource-json.interface.ts
Expand Up @@ -26,7 +26,7 @@ export default interface ResourceJSON {
/**
* Parent name
*/
name: string;
name: string | null;
/**
* Parent icon
*/
Expand Down
2 changes: 1 addition & 1 deletion src/backend/decorators/resource-options.interface.ts
Expand Up @@ -78,7 +78,7 @@ export interface ResourceOptions {
* If null is given. Parent wont be displayed
*/
parent?: {
name?: string;
name?: string | null;
icon?: string;
} | string | null;
/**
Expand Down
46 changes: 28 additions & 18 deletions src/frontend/components/app/sidebar/sidebar-resource-section.tsx
@@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { Box, cssClass, Navigation } from '@admin-bro/design-system'
import { Box, cssClass, Navigation, NavigationElementWithChildrenProps } from '@admin-bro/design-system'
import { useHistory, useLocation } from 'react-router'
import { useTranslation } from '../../../hooks/use-translation'
import groupResources from './utils/group-resources'
Expand Down Expand Up @@ -41,24 +41,34 @@ const SidebarResourceSectionOriginal: FC<SidebarResourceSectionProps> = ({ resou
const history = useHistory()
const location = useLocation()

const elements = groupResources(resources).map((element, index) => ({
...element,
onClick: (): void => setOpenElements({
...openElements,
[index]: !openElements[index],
}),
isOpen: !!openElements[index],
elements: element.elements?.map(subElement => ({
...subElement,
onClick: (event): void => {
if (subElement.href) {
event.preventDefault()
history.push(subElement.href)
}
},
isSelected: isSelected(subElement.href, location),
})),
const enrichElement = ((
subElement: NavigationElementWithChildrenProps,
): NavigationElementWithChildrenProps => ({
...subElement,
onClick: (event): void => {
if (subElement.href) {
event.preventDefault()
history.push(subElement.href)
}
},
isSelected: isSelected(subElement.href, location),
}))

const elements = groupResources(resources).map((element, index) => {
if (!element.elements || !element.elements.length) {
return enrichElement(element)
}
return {
...element,
onClick: (): void => setOpenElements({
...openElements,
[index]: !openElements[index],
}),
isOpen: !!openElements[index],
elements: element.elements?.map(enrichElement),
}
})

const { translateLabel } = useTranslation()

return (
Expand Down
19 changes: 0 additions & 19 deletions src/frontend/components/app/sidebar/styled/sidebar-link.styled.tsx

This file was deleted.

Expand Up @@ -5,33 +5,40 @@ import factory from '../../../spec/factory'

import groupResources from './group-resources'

describe.only('groupResources', () => {
describe('groupResources', () => {
let resources: Array<ResourceJSON>
let dashboard: ResourceJSON
let grouped: NavigationProps['elements']
let parent1: ResourceJSON['parent']
let parent2: ResourceJSON['parent']

beforeEach(async () => {
parent1 = { name: 'Volvo', icon: 'volvo' }
parent2 = { name: 'Audi', icon: 'audi' }
dashboard = await factory.build<ResourceJSON>('ResourceJSON', { parent: null })

resources = [
await factory.build<ResourceJSON>('ResourceJSON', { parent: parent1 }),
await factory.build<ResourceJSON>('ResourceJSON', { parent: parent1 }),
await factory.build<ResourceJSON>('ResourceJSON', { parent: parent1 }),
await factory.build<ResourceJSON>('ResourceJSON', { parent: parent2 }),
dashboard,
]

grouped = groupResources(resources)
})

it('groups nested resources into parents', async () => {
expect(grouped.length).to.equal(2)
expect(grouped.length).to.equal(3)
expect(grouped[0].label).to.eq(parent1?.name)
expect(grouped[1].label).to.eq(parent2?.name)
expect(grouped[2].label).to.eq(dashboard.name)
})

it('moves all nested elements to given parent', () => {
console.log({ grouped })
console.log({ 0: grouped[0].elements })

expect(grouped[0].elements?.length).to.eq(3)
expect(grouped[0].elements?.[0].label).to.eq(resources[0].name)
})
Expand Down
31 changes: 23 additions & 8 deletions src/frontend/components/app/sidebar/utils/group-resources.ts
Expand Up @@ -12,18 +12,33 @@ const resourceToNavigationElement = (
export default (resources: Array<ResourceJSON>): NavigationProps['elements'] => {
const visibleResources = resources.filter(res => res.href)
const map = visibleResources.reduce((memo, resource) => {
const key = resource.parent?.name || ''
if (memo[key]) {
// in case resource has the same name as parent we namespace it wit "resource-""
const key = resource.parent?.name || ['resource', resource.name].join('-')

if (!resource.parent || resource.parent.name === null) {
memo[key] = resource
} else if (resource.parent?.name && memo[key]) {
memo[key].push(resource)
} else {
memo[key] = [resource]
}
memo[key].icon = resource.parent?.icon

memo[key].icon = memo[key].icon || resource.parent?.icon
return memo
}, {})
return Object.keys(map).map(parentName => ({
label: parentName,
icon: map[parentName].icon,
elements: map[parentName].map(resourceToNavigationElement),
}))

return Object.keys(map).map((parentName) => {
if (!Array.isArray(map[parentName])) {
return {
...resourceToNavigationElement(map[parentName]),
icon: map[parentName].icon,
elements: [],
}
}
return {
label: parentName,
icon: map[parentName].icon,
elements: map[parentName].map(resourceToNavigationElement),
}
})
}

0 comments on commit 28e0a5d

Please sign in to comment.