Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Singleton with translations #6

Open
jdwillemse opened this issue Jul 10, 2020 · 18 comments
Open

Singleton with translations #6

jdwillemse opened this issue Jul 10, 2020 · 18 comments
Assignees

Comments

@jdwillemse
Copy link

I'm trying to combine this plugin with the setup for singleton document described here. When I do this the translation tab disappears across all documents. I'm using document level translations.

Assuming the settings document needs to be translated too, how do I rewrite this to make it work?

import S from "@sanity/desk-tool/structure-builder";
 
export default () =>
  S.list()
    .title('Content')
    .items([
      S.listItem()
        .title('Site settings')
        .child(
          S.document()
            .schemaType('siteSettings')
            .documentId('siteSettings')
        ),
      // Add a visual divider (optional)
      S.divider(),
      // List out the rest of the document types, but filter out the config type
      ...S.documentTypeListItems()
        .filter(listItem => !['siteSettings'].includes(listItem.getId()))
    ])
@LiamMartens
Copy link
Owner

LiamMartens commented Jul 13, 2020

So technically the problem is that when translating documents you are creating new ones, which conflicts with the whole singleton thing. However I did find a way around it as follows:

import S from '@sanity/desk-tool/structure-builder';
import homepage from './schemas/homepage';
import * as Structure from 'sanity-plugin-intl-input/lib/structure';

export const getDefaultDocumentNode = (props) => {
  return Structure.getDefaultDocumentNode(props);
};

export default () => {
  return S.list()
    .id('__root__')
    .title('Content')
    .items([
      S.listItem()
        .id('siteSettings')
        .title('Site Settings')
        .schemaType('siteSettings')
        .child(
          S.documentList()
            .id('siteSettings')
            .title('Site Settings')
            .schemaType('siteSettings')
            .filter('_id == $id && _type == $type')
            .params({
              id: 'siteSettings',
              type: 'siteSettings',
            })
            .menuItems([
              {
                title: 'Create new',
                intent: {
                  type: 'create',
                  params: {
                    id: 'siteSettings',
                    type: 'siteSettings',
                    template: 'siteSettings'
                  }
                }
              }
            ])
        ),
        ...Structure.getFilteredDocumentTypeListItems().filter(l => !['siteSettings'].includes(l.getId())),
    ])
};

Some explanation to this:
Basically we are keeping the same structure as usual so Sanity can resolve the structure for non-singleton's (which means a url like /desk/documentType/documentId)
But in the document list we are only showing the document with id siteSettings (hence we will always only show a single document). Now when this document does not exist yet, we will need a way to create it, but only the one with the fixed id. To do this we can create a menu item with the intent to create a document with a fixed id. This way everything should work as expected and you will still keep the singleton-like behavior.

Let me know if this works for you!

Example below
Peek 2020-07-13 21-59

@jdwillemse
Copy link
Author

Thanks for getting back to me on this issue.

The solution you propose is essentially the same as the default option. It is still possible to create multiple of the singleton doc and the user has to click through a secondary document list of one item. I'm trying to avoid the list view completely so clicking the content type leads directly to the page.

I understand why this tricky with the intl plugin, but singletons are a very standard part of all our projects so I'll need to keep looking for a solution.

Thanks again for the help

@LiamMartens
Copy link
Owner

@jdwillemse No you can still only create the one with the fixed ID (aka singleton) because the "Create new" action is fixed to the ID with this structure. However I am aware it is slightly more cumbersome. I am talking to some guys at Sanity regarding conflicts with custom desk structure but have not been able to find a resolution yet.

@tom2strobl
Copy link

@LiamMartens I'm trying to use it as you suggest up there (even though the extra pane for the single document is really weird/ugly), but I'm actually running into the issue of editor/translations-tabs not showing up on the singleton, even with your suggested solution.

This is my deskStructure (the commented out part is how I would have included the singleton without the intl-plugin, ignore the base settings, these are non-i18n):

import S from '@sanity/desk-tool/structure-builder'
import * as Structure from 'sanity-plugin-intl-input/lib/structure'

export const getDefaultDocumentNode = (props) => {
  if (props.schemaType === 'page') {
    return S.document().views(Structure.getDocumentNodeViewsForSchemaType(props.schemaType))
  }
  return S.document()
}

export default () => {
  const items = Structure.getFilteredDocumentTypeListItems()
  return S.list()
    .title('Content')
    .items([
      S.listItem().title('Base Settings').icon(GlobalIcon).child(S.document().schemaType('base').documentId('base')),

      // S.listItem()
      //   .title('Global Settings')
      //   .icon(HomeIcon)
      //   .child(S.document().schemaType('global').documentId('global')),

      S.listItem()
        .id('global')
        .title('Global Settings')
        .icon(HomeIcon)
        .schemaType('global')
        .child(
          S.documentList()
            .id('global')
            .title('Global Settings')
            .schemaType('global')
            .filter('_id == $id && _type == $type')
            .params({
              id: 'global',
              type: 'global'
            })
            .menuItems([
              {
                title: 'Create new',
                intent: {
                  type: 'create',
                  params: {
                    id: 'global',
                    type: 'global',
                    template: 'global'
                  }
                }
              }
            ])
        ),

      S.divider(),

      ...items.filter((item) => !['base', 'global'].includes(item.spec.id))
    ])
}

Since i18n works on other schemas in the same project and I'm using a global config with i18n: true, I'm kinda confident the config is fine (it's using GROQ for the languages property if that makes a difference for the singleton?).

@tom2strobl
Copy link

I guess the other way to do it is to nest all document fields into one object and throwing options: { i18n: true } on it. Although it seems like it kills fieldset borders and does some padding/margin-weirdness (https://www.dropbox.com/s/g5bp6u1aodnqpej/Screenshot%202020-07-28%2016.11.10.png?dl=0)

@ylokesh
Copy link

ylokesh commented Feb 4, 2021

@LiamMartens @tom2strobl I am facing same issue where in, Translation tab disappears for article documents appearing 2 levels deep.

Any pointers would be really helpful.

PS: I have tried the approach given above but it somehow it didn't work for me.

export default () => {
	return S.list()
		.id('__root__')
		.title('Content')
		.items([
		S.divider(),
		S.listItem()
			.title('Articles')
			.icon(FaNewspaper)
			.child(
			S.list()
				.title('Article Type')
				.items([
					getArticlePreviews('featureArticle').title('Feature'),
					getArticlePreviews('galleryArticle').title('Gallery'),
					getArticlePreviews('howToArticle').title('How To')
				])
			)]
		)
}

const getArticlePreviews = type =>
	S.documentTypeListItem(type).child(
		S.documentTypeList(type).child(docId =>
		S.document()
			.id(docId)
			.schemaType(type)
			.views([
			S.view.form()
			])
		)
	)

@saulhardman
Copy link

Although it seems like it kills fieldset borders and does some padding/margin-weirdness (https://www.dropbox.com/s/g5bp6u1aodnqpej/Screenshot%202020-07-28%2016.11.10.png?dl=0)

I'm experiencing the same issue and I'm wondering what the solution is? Do we need to write (or overwrite) a custom component for nested objects like this?

Any help pointing us in the right direction would be great @LiamMartens 👍

@LiamMartens
Copy link
Owner

@saulhardman Sorry I have not been able to look into this deeper. I might have to update the component that's being used as it is most likely outdated according to updates Sanity has received now. I try to look at it this week.

@LiamMartens
Copy link
Owner

@ylokesh I need to have a look at this 😬 combining desk structures is a bit "hacky" at the moment Imo

@LiamMartens LiamMartens self-assigned this Mar 9, 2021
@saulhardman
Copy link

@LiamMartens no need to apologise – thanks for taking a look and let us know if there's any way we can help out.

@LiamMartens
Copy link
Owner

@ylokesh you also have the getDefaultDocumentNode export in your deskStructure?

@LiamMartens
Copy link
Owner

@tom2strobl very late answer but in your case I believe it is because of your getDefaultDocumentNode export. You are limiting it to the "page" schemaType, but your document has a "global" schemaType

@LiamMartens
Copy link
Owner

@saulhardman I published a beta version 5.1.0-beta.0 where I updated the component to be in line with the latest Sanity updates. I need to do some more testing before releasing but it's there

@saulhardman
Copy link

@saulhardman I published a beta version 5.1.0-beta.0 where I updated the component to be in line with the latest Sanity updates. I need to do some more testing before releasing but it's there

Hi @LiamMartens, thank you very much 👍 I'll try that version out and get back to you.

@saulhardman
Copy link

Hey @LiamMartens, just following up to confirm that the updates you made in 5.1.0-beta.0 are working for me – thanks again 😊

@alexbchr
Copy link

Good to know @saulhardman that it works well for you! Can you share your solution in this thread please?

Also, @LiamMartens it would be great to have some documentation on this use case in the documentation, as I feel it is a very common use case.

@hacknug
Copy link
Contributor

hacknug commented Jul 1, 2021

Good to know @saulhardman that it works well for you! Can you share your solution in this thread please?

This isn't exactly what you asked for but here's what my deskStructure.js looks like after setting i18n up a couple weeks ago:

// deskStructure.js

import S from '@sanity/desk-tool/structure-builder'
import * as Structure from 'sanity-plugin-intl-input/lib/structure'
import SocialPreview from 'part:social-preview/component'
// import { toPlainText } from 'part:social-preview/utils'
import { BiCookie } from '@hacknug/react-icons/bi'
import {
  RiTranslate, RiFileLine, RiFolderShieldLine, RiFileDownloadLine,
  RiBriefcaseLine, RiArticleLine, RiUserLine, RiPriceTagLine, RiLayoutBottomLine, RiSettingsLine,
} from '@hacknug/react-icons/ri'

const pagesItems = [
  { title: 'Home', id: 'frontpage', schema: 'frontpage' },
  { title: 'About Us', id: 'about', schema: 'about' },
  { title: 'Blog', id: 'blog', schema: 'blog' },
  { title: 'Contact', id: 'contact', schema: 'contact' },
]

export const getDefaultDocumentNode = (props) => {
  return (![
    ...pagesItems.map(({ schema }) => schema),
    'blogPost', 'blogAuthor', 'blogTag',
  ].includes(props.schemaType))
    ? S.document()
    : S.document().views([
      ...Structure.getDocumentNodeViewsForSchemaType(props.schemaType),
      S.view.component(SocialPreview({
        prepareFunction: ({ seo }) => ({
          title: seo?.seo_text,
          description: seo?.seo_desc,
          ogImage: seo?.seo_image,
          siteUrl: 'https://www.example.com',
        }),
      })).title('Social & SEO'),
    ])
}

export default () => {
  const items = Structure.getFilteredDocumentTypeListItems()
  const getLocalizedPosts = (type) => items.find((item) => [type].includes(item.spec.id)).getChild()

  return S.list().title('Content').items([
    ...pagesItems.map(({ id, title, schema, icon = RiFileLine }) => {
      return S.listItem().title(`${title} Page`).icon(icon).child(getLocalizedPosts(schema))
    }),

    S.divider(),

    S.listItem().title('Blog Articles').icon(RiArticleLine).child(getLocalizedPosts('blogPost')),
    S.listItem().title('Blog Authors').icon(RiUserLine).child(getLocalizedPosts('blogAuthor')),
    S.listItem().title('Blog Tags').icon(RiPriceTagLine).child(getLocalizedPosts('blogTag')),

    S.divider(),

    S.listItem().title('Site Settings').icon(RiSettingsLine)
      .child(S.document().schemaType('siteSettings').documentId('siteSettings').title('Site Settings')),

    S.divider(),

    items[0].title('Translation Manager').icon(RiTranslate),
  ])
}

It doesn't keep the whole singleton implementation but it is okay in my case. I also ended up leaving these pageItems ungrouped (they were all previously grouped under a Pages folder).

Only reason for this is I didn't manage to make everything work while keeping the same structure. The current implementation made it easy to keep everything I had in place pretty much the same so that's a win if you ask me.

@sinclairnick
Copy link

Hi @LiamMartens is there any update on this one? I'm using version 5.2.1 to no avail.

There is a related issue on the sanity repo which seems relevant.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants