Skip to content

Commit

Permalink
[desk-tool] Experimental feature: actions (#992)
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoerge committed Oct 4, 2018
1 parent 97a1d78 commit f940f60
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 48 deletions.
4 changes: 4 additions & 0 deletions packages/@sanity/base/sanity.json
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@
"implements": "part:@sanity/base/util/draft-utils",
"path": "util/draftUtils.js"
},
{
"implements": "part:@sanity/base/util/document-action-utils",
"path": "util/documentActionUtils.js"
},
{
"implements": "part:@sanity/base/theme/variables-style",
"path": "styles/variables.css"
Expand Down
36 changes: 36 additions & 0 deletions packages/@sanity/base/src/util/documentActionUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {difference} from 'lodash'

const ACTIONS_FLAG = '__experimental_actions'

const DEFAULT_ACTIONS = ['create', 'update', 'delete', 'publish']
const VALID_ACTIONS = DEFAULT_ACTIONS

const readActions = schemaType =>
ACTIONS_FLAG in schemaType ? schemaType[ACTIONS_FLAG] : DEFAULT_ACTIONS

const validateActions = (typeName, actions) => {
if (!Array.isArray(actions)) {
throw new Error(
`The value of <type>.${ACTIONS_FLAG} should be an array with any of the actions ${VALID_ACTIONS.join(
', '
)}`
)
}
const invalid = difference(actions, VALID_ACTIONS)
if (invalid.length > 0) {
throw new Error(
`Invalid action${
invalid.length > 1 ? 's' : ''
} configured for schema type "${typeName}": ${invalid.join(
', '
)}. Valid actions are: ${VALID_ACTIONS.join(', ')}`
)
}
return actions
}

export const resolveEnabledActions = schemaType =>
validateActions(schemaType.name, readActions(schemaType))

export const isActionEnabled = (schemaType, action) =>
resolveEnabledActions(schemaType).includes(action)
20 changes: 13 additions & 7 deletions packages/@sanity/default-layout/src/components/DefaultLayout.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {RouteScope, withRouterHOC} from 'part:@sanity/base/router'
import absolutes from 'all:part:@sanity/base/absolutes'
import ToolSwitcher from 'part:@sanity/default-layout/tool-switcher'
import {HAS_SPACES} from '../util/spaces'
import {isActionEnabled} from 'part:@sanity/base/util/document-action-utils'
import styles from './styles/DefaultLayout.css'
import RenderTool from './RenderTool'
import Navigation from './Navigation'
Expand Down Expand Up @@ -106,12 +107,15 @@ export default withRouterHOC(
const {tools, router} = this.props
const {createMenuIsOpen, mobileMenuIsOpen} = this.state

const TYPE_ITEMS = dataAspects.getInferredTypes().map(typeName => ({
key: typeName,
name: typeName,
title: dataAspects.getDisplayName(typeName),
icon: dataAspects.getIcon(typeName)
}))
const TYPE_ITEMS = dataAspects
.getInferredTypes()
.filter(typeName => isActionEnabled(schema.get(typeName), 'create'))
.map(typeName => ({
key: typeName,
name: typeName,
title: dataAspects.getDisplayName(typeName),
icon: dataAspects.getIcon(typeName)
}))

const modalActions = TYPE_ITEMS.map(item => ({
title: item.title,
Expand Down Expand Up @@ -198,7 +202,9 @@ export default withRouterHOC(
<SanityStudioLogo />
</a>

{absolutes.map((Abs, i) => <Abs key={i} />)}
{absolutes.map((Abs, i) => (
<Abs key={i} />
))}
</div>
)
}
Expand Down
14 changes: 9 additions & 5 deletions packages/@sanity/desk-tool/src/pane/DocumentsListPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
isPublishedId,
getDraftId
} from 'part:@sanity/base/util/draft-utils'
import {isActionEnabled} from 'part:@sanity/base/util/document-action-utils'

import {combineLatest} from 'rxjs'
import {map, tap} from 'rxjs/operators'
import styles from './styles/DocumentsListPane.css'
Expand Down Expand Up @@ -285,6 +287,7 @@ export default withRouterHOC(
const items = removePublishedWithDrafts(result ? result.documents : [])

if (!loading && !hasItems(items)) {
const schemaType = schema.get(typeName)
return (
<div className={styles.empty}>
<div>
Expand All @@ -294,11 +297,12 @@ export default withRouterHOC(
: 'No documents matching this filter found.'}
</h3>

{typeName && (
<Button color="primary" icon={PlusIcon} onClick={this.handleCreateNew}>
New {schema.get(typeName).title}
</Button>
)}
{typeName &&
isActionEnabled(schemaType, 'create') && (
<Button color="primary" icon={PlusIcon} onClick={this.handleCreateNew}>
New {schemaType.title}
</Button>
)}
</div>
</div>
)
Expand Down
56 changes: 30 additions & 26 deletions packages/@sanity/desk-tool/src/pane/Editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {Tooltip} from '@sanity/react-tippy'
import {withRouterHOC} from 'part:@sanity/base/router'
import {PreviewFields} from 'part:@sanity/base/preview'
import {getPublishedId, newDraftFrom} from 'part:@sanity/base/util/draft-utils'
import {resolveEnabledActions, isActionEnabled} from 'part:@sanity/base/util/document-action-utils'
import Spinner from 'part:@sanity/components/loading/spinner'
import Button from 'part:@sanity/components/buttons/default'
import FormBuilder from 'part:@sanity/form-builder'
Expand Down Expand Up @@ -131,15 +132,16 @@ const getProductionPreviewItem = (draft, published) => {
)
}

const getMenuItems = (draft, published, isLiveEditEnabled) =>
const getMenuItems = (enabledActions, draft, published, isLiveEditEnabled) =>
[
getProductionPreviewItem,
getDiscardItem,
getUnpublishItem,
getDuplicateItem,
enabledActions.includes('delete') && getDiscardItem,
enabledActions.includes('delete') && getUnpublishItem,
enabledActions.includes('create') && getDuplicateItem,
getInspectItem,
getDeleteItem
enabledActions.includes('delete') && getDeleteItem
]
.filter(Boolean)
.map(fn => fn(draft, published, isLiveEditEnabled))
.filter(Boolean)

Expand Down Expand Up @@ -537,26 +539,27 @@ export default withRouterHOC(
</Button>
</Tooltip>
)}
{!this.isLiveEditEnabled() && (
<Tooltip
arrow
theme="light"
className={styles.publishButton}
title={
errors.length > 0
? 'Fix errors before publishing'
: `${published ? 'Publish changes' : 'Publish'} (Ctrl+Alt+P)`
}
>
<Button
disabled={isReconnecting || !draft || errors.length > 0}
onClick={this.handlePublishRequested}
color="primary"
{isActionEnabled(type, 'publish') &&
!this.isLiveEditEnabled() && (
<Tooltip
arrow
theme="light"
className={styles.publishButton}
title={
errors.length > 0
? 'Fix errors before publishing'
: `${published ? 'Publish changes' : 'Publish'} (Ctrl+Alt+P)`
}
>
Publish
</Button>
</Tooltip>
)}
<Button
disabled={isReconnecting || !draft || errors.length > 0}
onClick={this.handlePublishRequested}
color="primary"
>
Publish
</Button>
</Tooltip>
)}
</div>
)
}
Expand Down Expand Up @@ -610,12 +613,13 @@ export default withRouterHOC(
)
}

const enabledActions = resolveEnabledActions(type)
return (
<Pane
styles={this.props.paneStyles}
title={this.getTitle(value)}
onAction={this.handleMenuAction}
menuItems={getMenuItems(draft, published, this.isLiveEditEnabled())}
menuItems={getMenuItems(enabledActions, draft, published, this.isLiveEditEnabled())}
renderActions={this.renderActions}
onMenuToggle={this.handleMenuToggle}
isSelected // last pane is always selected for now
Expand Down Expand Up @@ -659,7 +663,7 @@ export default withRouterHOC(
value={draft || published || {_type: type.name}}
type={type}
filterField={filterField}
readOnly={isReconnecting}
readOnly={isReconnecting || !isActionEnabled(type, 'update')}
onBlur={this.handleBlur}
onFocus={this.handleFocus}
focusPath={focusPath}
Expand Down
32 changes: 22 additions & 10 deletions packages/@sanity/structure/src/documentTypeListItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {DEFAULT_SELECTED_ORDERING_OPTION} from './Sort'
import {DocumentListBuilder} from './DocumentList'
import {ListItemBuilder} from './ListItem'
import {EditorBuilder} from './Editor'
import {isActionEnabled} from './parts/documentActionUtils'

const PlusIcon = getPlusIcon()
const ListIcon = getListIcon()
Expand Down Expand Up @@ -41,6 +42,9 @@ export function getDocumentTypeList(
const type = schema.get(typeName)
const resolver = getDataAspectsForSchema(schema)
const title = resolver.getDisplayName(typeName)

const canCreate = isActionEnabled(type, 'create')

return new DocumentListBuilder()
.id(typeName)
.title(title)
Expand All @@ -66,11 +70,15 @@ export function getDocumentTypeList(
)
.menuItems([
// Create new (from action button)
new MenuItemBuilder()
.title(`Create new ${title}`)
.icon(PlusIcon)
.intent({type: 'create', params: {type: typeName}})
.showAsAction({whenCollapsed: true}),
...(canCreate
? [
new MenuItemBuilder()
.title(`Create new ${title}`)
.icon(PlusIcon)
.intent({type: 'create', params: {type: typeName}})
.showAsAction({whenCollapsed: true})
]
: []),

// Sort by <Y>
...getOrderingMenuItemsForSchemaType(type),
Expand All @@ -91,10 +99,14 @@ export function getDocumentTypeList(
.params({layout: 'detail'}),

// Create new (from menu)
new MenuItemBuilder()
.group('actions')
.title('Create new…')
.icon(PlusIcon)
.intent({type: 'create', params: {type: typeName}})
...(canCreate
? [
new MenuItemBuilder()
.group('actions')
.title('Create new…')
.icon(PlusIcon)
.intent({type: 'create', params: {type: typeName}})
]
: [])
])
}
17 changes: 17 additions & 0 deletions packages/@sanity/structure/src/parts/documentActionUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {SchemaType} from './schema'

export interface DocumentActionUtils {
isActionEnabled(schema: SchemaType, action: string[]): boolean
resolveEnabledActions(schema: SchemaType): string[]
}

// We are lazy-loading the part to work around typescript trying to resolve it
export const isActionEnabled = (() => {
const documentActionUtils = require('part:@sanity/base/util/document-action-utils')
return (type: SchemaType, action: string) => documentActionUtils.isActionEnabled(type, action)
})()

export const resolveEnabledActions = (() => {
const documentActionUtils = require('part:@sanity/base/util/document-action-utils')
return (type: SchemaType) => documentActionUtils.resolveEnabledActions(type)
})()
14 changes: 14 additions & 0 deletions packages/test-studio/schemas/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export default {
name: 'actionsTest',
type: 'document',
title: 'Experimental actions test',
// toggle these to test various combinations of ['create', 'update', 'delete', 'publish']
__experimental_actions: [],
fields: [
{
name: 'title',
title: 'Title',
type: 'string'
}
]
}
2 changes: 2 additions & 0 deletions packages/test-studio/schemas/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import previewMediaTest from './previewMediaTest'
import species from './species'
import date from './date'
import invalidPreviews from './invalidPreviews'
import actions from './actions'

export default createSchema({
name: 'test-examples',
Expand All @@ -63,6 +64,7 @@ export default createSchema({
date,
richDateTest,
validation,
actions,
topLevelArrayType,
topLevelPrimitiveArrayType,
arrays,
Expand Down

0 comments on commit f940f60

Please sign in to comment.