Skip to content

Commit 42ef3cd

Browse files
authored
Merge pull request #38 from Q42/feature/validate-parent-language
adds check for language
2 parents 7da01a8 + 3fc5681 commit 42ef3cd

File tree

10 files changed

+69
-43
lines changed

10 files changed

+69
-43
lines changed

examples/studio-i18n/page-tree-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ export const pageTreeConfig: PageTreeConfig = {
77
titleFieldName: 'title',
88
documentInternationalization: {
99
supportedLanguages: ['nl', 'en', 'fr'],
10+
documentLanguageShouldMatchParent: true,
1011
}
1112
};

src/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { SanityClient } from 'sanity';
22

33
import { getAllPageMetadata } from './helpers/page-tree';
4-
import { getRawPageMetadataQuery } from './queries';
4+
import { getAllRawPageMetadataQuery } from './queries';
55
import { PageMetadata, PageTreeConfig } from './types';
66

77
export type { PageMetadata } from './types';
@@ -25,7 +25,7 @@ class PageTreeClient {
2525
}
2626

2727
public async getAllPageMetadata(): Promise<PageMetadata[]> {
28-
const rawPageMetadata = await this.client.fetch(getRawPageMetadataQuery(this.config));
28+
const rawPageMetadata = await this.client.fetch(getAllRawPageMetadataQuery(this.config));
2929
return getAllPageMetadata(this.config, rawPageMetadata);
3030
}
3131
}

src/hooks/usePageTree.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { useMemo } from 'react';
22
import { useListeningQuery } from 'sanity-plugin-utils';
33

44
import { mapRawPageMetadatasToPageTree } from '../helpers/page-tree';
5-
import { getRawPageMetadataQuery } from '../queries';
5+
import { getAllRawPageMetadataQuery } from '../queries';
66
import { PageTreeConfig, RawPageMetadata } from '../types';
77

88
export const usePageTree = (config: PageTreeConfig) => {
9-
const { data, loading } = useListeningQuery<RawPageMetadata[]>(getRawPageMetadataQuery(config), {
9+
const { data, loading } = useListeningQuery<RawPageMetadata[]>(getAllRawPageMetadataQuery(config), {
1010
options: { apiVersion: config.apiVersion },
1111
});
1212

src/hooks/usePageTreeItem.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { useMemo } from 'react';
33
import { useListeningQuery } from 'sanity-plugin-utils';
44

55
import { getAllPageMetadata } from '../helpers/page-tree';
6-
import { getRawPageMetadataQuery } from '../queries';
6+
import { getAllRawPageMetadataQuery } from '../queries';
77
import { PageTreeConfig, RawPageMetadata } from '../types';
88

99
export const usePageTreeItem = (documentId: string, config: PageTreeConfig, perspective?: ClientPerspective) => {
10-
const { data, loading } = useListeningQuery<RawPageMetadata[]>(getRawPageMetadataQuery(config), {
10+
const { data, loading } = useListeningQuery<RawPageMetadata[]>(getAllRawPageMetadataQuery(config), {
1111
options: { apiVersion: config.apiVersion, perspective },
1212
});
1313

src/next.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { FilteredResponseQueryOptions, SanityClient } from 'next-sanity';
22

33
import { getAllPageMetadata } from './helpers/page-tree';
4-
import { getRawPageMetadataQuery } from './queries';
4+
import { getAllRawPageMetadataQuery } from './queries';
55
import { PageMetadata, PageTreeConfig } from './types';
66

77
export type { PageMetadata } from './types';
@@ -29,7 +29,7 @@ class NextPageTreeClient {
2929

3030
public async getAllPageMetadata(): Promise<PageMetadata[]> {
3131
const rawPageMetadata = await this.client.fetch(
32-
getRawPageMetadataQuery(this.config),
32+
getAllRawPageMetadataQuery(this.config),
3333
undefined,
3434
this.defaultSanityFetchOptions ?? {},
3535
);

src/queries/index.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
import { getLanguageFieldName } from '../helpers/config';
22
import { PageTreeConfig } from '../types';
33

4-
export const getRawPageMetadataQuery = (config: PageTreeConfig) => `*[_type in [${Object.values(config.pageSchemaTypes)
4+
export const getAllRawPageMetadataQuery = (config: PageTreeConfig) => `*[_type in [${Object.values(
5+
config.pageSchemaTypes,
6+
)
57
.map(key => `"${key}"`)
68
.join(', ')}]]{
9+
${rawPageMetadataFragment(config)}
10+
}`;
11+
12+
export const getRawPageMetadataQuery = (documentId: string, config: PageTreeConfig) => `*[_id == "${documentId}"]{
13+
${rawPageMetadataFragment(config)}
14+
}`;
15+
16+
export const rawPageMetadataFragment = (config: PageTreeConfig) => `
717
_id,
818
_type,
919
_updatedAt,
1020
parent,
1121
slug,
1222
title,
13-
${getLanguageFieldName(config) ?? ''}
14-
}`;
15-
16-
export const getDocumentTypeQuery = (documentId: string) => `*[_id == "${documentId}"]{
17-
_type
18-
}`;
23+
${getLanguageFieldName(config) ?? ''}`;

src/schema/definePageType.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { defineField, defineType, DocumentDefinition, SlugOptions } from 'sanity
44
import { PageTreeField } from '../components/PageTreeField';
55
import { SlugField } from '../components/SlugField';
66
import { PageTreeConfig } from '../types';
7-
import { allowedParentValidator } from '../validators/parent-validator';
7+
import { parentValidator } from '../validators/parent-validator';
88
import { slugValidator } from '../validators/slug-validator';
99

1010
type Options = {
@@ -67,7 +67,7 @@ const basePageFields = (config: PageTreeConfig, options: Options, ownType: Docum
6767
title: 'Parent page',
6868
type: 'reference',
6969
to: getPossibleParentsFromConfig(config, ownType).map(type => ({ type })),
70-
validation: Rule => Rule.required().custom(allowedParentValidator(config, ownType.name)),
70+
validation: Rule => Rule.required().custom(parentValidator(config, ownType.name)),
7171
group: options.fieldsGroupName,
7272
components: {
7373
field: props => PageTreeField({ ...props, config, mode: 'select-parent' }),

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ export type PageTreeConfig = {
6060
supportedLanguages: string[];
6161
/* Optional field name of the language field, defaults to "language" */
6262
languageFieldName?: string;
63+
/* Adds validation check to ensure that the language of the document matches the language of the parent document. Default: false */
64+
documentLanguageShouldMatchParent?: boolean;
6365
};
6466
};
6567

src/validators/parent-validator.ts

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,60 @@
11
import { Reference, ValidationContext } from 'sanity';
22

3-
import { getDocumentTypeQuery } from '../queries';
4-
import { PageTreeConfig, RawPageMetadata, SanityRef } from '../types';
3+
import { getLanguageFieldName } from '../helpers/config';
4+
import { getRawPageMetadataQuery } from '../queries';
5+
import { PageTreeConfig, RawPageMetadata } from '../types';
56

67
/**
78
* Validates that the slug is unique within the parent page and therefore that entire the path is unique.
89
*/
9-
export const allowedParentValidator =
10+
export const parentValidator =
1011
(config: PageTreeConfig, ownType: string) =>
11-
async (selectedParent: Reference | undefined, context: ValidationContext) => {
12-
const allowedParents = config.allowedParents?.[ownType];
13-
14-
if (allowedParents === undefined) {
15-
return true;
16-
}
12+
async (selectedParentRef: Reference | undefined, context: ValidationContext) => {
13+
const client = context.getClient({ apiVersion: config.apiVersion });
1714

18-
const parentRef = context.document?.parent as SanityRef | undefined;
19-
if (!parentRef) {
15+
if (!selectedParentRef) {
2016
return true;
2117
}
2218

23-
const parentId = parentRef._ref;
19+
const parentId = selectedParentRef._ref;
20+
const selectedParent = (await client.fetch<RawPageMetadata[]>(getRawPageMetadataQuery(parentId, config)))[0];
2421

25-
if (parentId === undefined) {
26-
return true;
22+
const allowedParentValidation = allowedParentValidator(selectedParent, config, ownType);
23+
if (allowedParentValidation !== true) {
24+
return allowedParentValidation;
2725
}
2826

29-
const client = context.getClient({ apiVersion: config.apiVersion });
30-
const selectedParentType = (await client.fetch<Pick<RawPageMetadata, '_type'>[]>(getDocumentTypeQuery(parentId)))[0]
31-
?._type;
27+
return parentLanguageValidator(selectedParent, config, context);
28+
};
3229

33-
if (!selectedParentType) {
34-
return 'Unable to check the type of the selected parent.';
35-
}
30+
const allowedParentValidator = (selectedParent: RawPageMetadata, config: PageTreeConfig, ownType: string) => {
31+
const allowedParents = config.allowedParents?.[ownType];
3632

37-
if (!allowedParents.includes(selectedParentType)) {
38-
return `The parent of type "${selectedParentType}" is not allowed for this type of document.`;
33+
if (allowedParents === undefined) {
34+
return true;
35+
}
36+
37+
if (!allowedParents.includes(selectedParent._type)) {
38+
return `The parent of type "${selectedParent._type}" is not allowed for this type of document.`;
39+
}
40+
41+
return true;
42+
};
43+
44+
const parentLanguageValidator = (
45+
selectedParent: RawPageMetadata,
46+
config: PageTreeConfig,
47+
context: ValidationContext,
48+
) => {
49+
if (config.documentInternationalization?.documentLanguageShouldMatchParent) {
50+
const languageFieldName = getLanguageFieldName(config);
51+
const language = context.document?.[languageFieldName];
52+
const parentLanguage = selectedParent?.[languageFieldName];
53+
54+
if (language !== parentLanguage) {
55+
return 'The language of the parent must match the language of the document.';
3956
}
57+
}
4058

41-
return true;
42-
};
59+
return true;
60+
};

src/validators/slug-validator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { SlugValue, ValidationContext } from 'sanity';
22

33
import { DRAFTS_PREFIX } from '../helpers/page-tree';
4-
import { getRawPageMetadataQuery } from '../queries';
4+
import { getAllRawPageMetadataQuery } from '../queries';
55
import { PageTreeConfig, RawPageMetadata, SanityRef } from '../types';
66
import { getSanityDocumentId } from '../utils/sanity';
77

@@ -17,7 +17,7 @@ export const slugValidator =
1717
return true;
1818
}
1919

20-
const allPages = await client.fetch<RawPageMetadata[]>(getRawPageMetadataQuery(config));
20+
const allPages = await client.fetch<RawPageMetadata[]>(getAllRawPageMetadataQuery(config));
2121
const siblingPages = allPages.filter(page => page.parent?._ref === parentRef._ref);
2222

2323
const siblingPagesWithSameSlug = siblingPages

0 commit comments

Comments
 (0)