Skip to content

Commit

Permalink
Fix relationship displayMode incorrectly defaulting to count
Browse files Browse the repository at this point in the history
  • Loading branch information
emmatown committed Nov 18, 2022
1 parent 94e12b4 commit 675c4ab
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/popular-timers-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-6/core': patch
---

Fixes `ui.displayMode` in the `relationship` field incorrectly defaulting to `count` instead of `select`
74 changes: 50 additions & 24 deletions packages/core/src/fields/types/relationship/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,21 +138,52 @@ export const relationship =
.filter(x => x.search)
.map(x => x.key);

if (config.ui?.displayMode === 'select') {
if (config.ui?.displayMode === 'count') {
return {
refFieldKey: foreignFieldKey,
refListKey: foreignListKey,
many,
hideCreate,
displayMode: 'select',

// prefer the local definition to the foreign list, if provided
refLabelField: config.ui.labelField || refLabelField,
refSearchFields: config.ui.searchFields || refSearchFields,
displayMode: 'count',
refLabelField,
refSearchFields,
};
}

if (config.ui?.displayMode === 'cards') {
// prefer the local definition to the foreign list, if provided
const inlineConnectConfig =
typeof config.ui.inlineConnect === 'object'
? {
refLabelField: config.ui.inlineConnect.labelField ?? refLabelField,
refSearchFields: config.ui.inlineConnect?.searchFields ?? refSearchFields,
}
: {
refLabelField,
refSearchFields,
};

if (!(inlineConnectConfig.refLabelField in foreignListMeta.fieldsByKey)) {
throw new Error(
`The ui.inlineConnect.labelField option for field '${listKey}.${fieldKey}' uses '${inlineConnectConfig.refLabelField}' but that field doesn't exist.`
);
}

for (const searchFieldKey of inlineConnectConfig.refSearchFields) {
if (!(searchFieldKey in foreignListMeta.fieldsByKey)) {
throw new Error(
`The ui.inlineConnect.searchFields option for relationship field '${listKey}.${fieldKey}' includes '${searchFieldKey}' but that field doesn't exist.`
);
}

const field = foreignListMeta.fieldsByKey[searchFieldKey];
if (field.search) continue;

throw new Error(
`The ui.searchFields option for field '${listKey}.${fieldKey}' includes '${searchFieldKey}' but that field doesn't have a contains filter that accepts a GraphQL String`
);
}

return {
refFieldKey: foreignFieldKey,
refListKey: foreignListKey,
Expand All @@ -166,37 +197,32 @@ export const relationship =
inlineEdit: config.ui.inlineEdit ?? null,
inlineConnect: config.ui.inlineConnect ? true : false,

// prefer the local definition to the foreign list, if provided
...(typeof config.ui.inlineConnect === 'object'
? {
refLabelField: config.ui.inlineConnect.labelField ?? refLabelField,
refSearchFields: config.ui.inlineConnect?.searchFields ?? refSearchFields,
}
: {
refLabelField,
refSearchFields,
}),
...inlineConnectConfig,
};
}

if (!(refLabelField in foreignListMeta.fieldsByKey)) {
// prefer the local definition to the foreign list, if provided
const specificRefLabelField = config.ui?.labelField || refLabelField;
const specificRefSearchFields = config.ui?.searchFields || refSearchFields;

if (!(specificRefLabelField in foreignListMeta.fieldsByKey)) {
throw new Error(
`The ui.labelField option for field '${fieldKey}' uses '${refLabelField}' but that field doesn't exist.`
`The ui.labelField option for field '${listKey}.${fieldKey}' uses '${specificRefLabelField}' but that field doesn't exist.`
);
}

for (const searchFieldKey of refSearchFields) {
for (const searchFieldKey of specificRefSearchFields) {
if (!(searchFieldKey in foreignListMeta.fieldsByKey)) {
throw new Error(
`The ui.searchFields option for relationship field '${fieldKey}' includes '${searchFieldKey}' but that field doesn't exist.`
`The ui.searchFields option for relationship field '${listKey}.${fieldKey}' includes '${searchFieldKey}' but that field doesn't exist.`
);
}

const field = foreignListMeta.fieldsByKey[searchFieldKey];
if (field.search) continue;

throw new Error(
`The ui.searchFields option for field '${fieldKey}' includes '${searchFieldKey}' but that field doesn't have a contains filter that accepts a GraphQL String`
`The ui.searchFields option for field '${listKey}.${fieldKey}' includes '${searchFieldKey}' but that field doesn't have a contains filter that accepts a GraphQL String`
);
}

Expand All @@ -205,9 +231,9 @@ export const relationship =
refListKey: foreignListKey,
many,
hideCreate,
displayMode: 'count',
refLabelField,
refSearchFields,
displayMode: 'select',
refLabelField: specificRefLabelField,
refSearchFields: specificRefSearchFields,
};
},
};
Expand Down
176 changes: 176 additions & 0 deletions tests/api-tests/relationships/label-search-field-validation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { list } from '@keystone-6/core';
import { allowAll } from '@keystone-6/core/access';
import { getContext } from '@keystone-6/core/context';
import { integer, relationship, text } from '@keystone-6/core/fields';
import { apiTestConfig } from '../utils';

const Thing = list({
access: allowAll,
fields: {
name: text(),
other: text(),
notText: integer(),
},
});

test("labelField that doesn't exist is rejected with displayMode: select", () => {
expect(() =>
getContext(
apiTestConfig({
lists: {
A: list({
access: allowAll,
fields: {
something: relationship({
ref: 'Thing',
ui: {
labelField: 'doesNotExist',
},
}),
},
}),
Thing,
},
}),
{} as any
)
).toThrowErrorMatchingInlineSnapshot(
`"The ui.labelField option for field 'A.something' uses 'doesNotExist' but that field doesn't exist."`
);
});

test("labelField that doesn't exist is rejected with displayMode: cards", () => {
expect(() =>
getContext(
apiTestConfig({
lists: {
A: list({
access: allowAll,
fields: {
something: relationship({
ref: 'Thing',
ui: {
displayMode: 'cards',
cardFields: ['name'],
inlineConnect: { labelField: 'doesNotExist' },
},
}),
},
}),
Thing,
},
}),
{} as any
)
).toThrowErrorMatchingInlineSnapshot(
`"The ui.inlineConnect.labelField option for field 'A.something' uses 'doesNotExist' but that field doesn't exist."`
);
});

test("searchFields that don't exist are rejected with displayMode: select", () => {
expect(() =>
getContext(
apiTestConfig({
lists: {
A: list({
access: allowAll,
fields: {
something: relationship({
ref: 'Thing',
ui: {
searchFields: ['doesNotExist'],
},
}),
},
}),
Thing,
},
}),
{} as any
)
).toThrowErrorMatchingInlineSnapshot(
`"The ui.searchFields option for relationship field 'A.something' includes 'doesNotExist' but that field doesn't exist."`
);
});

test("searchFields that don't exist are rejected with displayMode: cards", () => {
expect(() =>
getContext(
apiTestConfig({
lists: {
A: list({
access: allowAll,
fields: {
something: relationship({
ref: 'Thing',
ui: {
displayMode: 'cards',
cardFields: ['name'],
inlineConnect: { labelField: 'name', searchFields: ['doesNotExist'] },
},
}),
},
}),
Thing,
},
}),
{} as any
)
).toThrowErrorMatchingInlineSnapshot(
`"The ui.inlineConnect.searchFields option for relationship field 'A.something' includes 'doesNotExist' but that field doesn't exist."`
);
});

test("searchFields that aren't searchable are rejected with displayMode: select", () => {
expect(() =>
getContext(
apiTestConfig({
lists: {
A: list({
access: allowAll,
fields: {
something: relationship({
ref: 'Thing',
ui: {
searchFields: ['notText'],
},
}),
},
}),
Thing,
},
}),
{} as any
)
).toThrowErrorMatchingInlineSnapshot(
`"The ui.searchFields option for field 'A.something' includes 'notText' but that field doesn't have a contains filter that accepts a GraphQL String"`
);
});

test("searchFields that aren't searchable are rejected with displayMode: cards", () => {
expect(() =>
getContext(
apiTestConfig({
lists: {
A: list({
access: allowAll,
fields: {
something: relationship({
ref: 'Thing',
ui: {
displayMode: 'cards',
cardFields: ['name'],
inlineConnect: { labelField: 'name', searchFields: ['notText'] },
},
}),
},
}),
Thing,
},
}),
{} as any
)
).toThrowErrorMatchingInlineSnapshot(
`"The ui.searchFields option for field 'A.something' includes 'notText' but that field doesn't have a contains filter that accepts a GraphQL String"`
);
});

0 comments on commit 675c4ab

Please sign in to comment.