From eeae38681d499837657e56d064875e7b4a4ea749 Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Tue, 27 Aug 2024 09:21:19 +0200 Subject: [PATCH 1/6] feat(studio): custom string input component with character count Composes the default string input rendering with a simple character count. Initially only used in SEO fields, but can be later applied where applicable. --- .../components/StringInputWithCharacterCount.tsx | 15 +++++++++++++++ studio/schemas/objects/seo.ts | 7 +++++++ 2 files changed, 22 insertions(+) create mode 100644 studio/components/StringInputWithCharacterCount.tsx diff --git a/studio/components/StringInputWithCharacterCount.tsx b/studio/components/StringInputWithCharacterCount.tsx new file mode 100644 index 000000000..782b70d19 --- /dev/null +++ b/studio/components/StringInputWithCharacterCount.tsx @@ -0,0 +1,15 @@ +import { Box, Stack, Text } from "@sanity/ui"; +import { StringInputProps } from "sanity"; + +export const StringInputWithCharacterCount = (props: StringInputProps) => { + const characterCount = props.value?.length ?? 0; + + return ( + + {props.renderDefault(props)} + + {characterCount} characters + + + ); +}; diff --git a/studio/schemas/objects/seo.ts b/studio/schemas/objects/seo.ts index 8f6bb4603..919b566c7 100644 --- a/studio/schemas/objects/seo.ts +++ b/studio/schemas/objects/seo.ts @@ -1,4 +1,5 @@ import { defineField } from "sanity"; +import { StringInputWithCharacterCount } from "studio/components/StringInputWithCharacterCount"; const seoFieldID = { title: "seoTitle", @@ -28,6 +29,9 @@ const seo = defineField({ .error("A title of minimum 15 characters is required"), Rule.max(70).error("A title cannot exceed 70 characters"), ], + components: { + input: StringInputWithCharacterCount, + }, }), defineField({ name: seoFieldID.description, @@ -43,6 +47,9 @@ const seo = defineField({ "A description of more than 160 characters has a lower chance of converting visitors", ), ], + components: { + input: StringInputWithCharacterCount, + }, }), defineField({ name: seoFieldID.keywords, From f8218eea0a04db06578cc42f35e0a8c6c35cbff2 Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Tue, 10 Sep 2024 15:46:50 +0200 Subject: [PATCH 2/6] feat(StringInputWithCharacterCount): max count --- .../components/StringInputWithCharacterCount.tsx | 16 ++++++++++++---- studio/schemas/objects/seo.ts | 6 ++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/studio/components/StringInputWithCharacterCount.tsx b/studio/components/StringInputWithCharacterCount.tsx index 782b70d19..852581088 100644 --- a/studio/components/StringInputWithCharacterCount.tsx +++ b/studio/components/StringInputWithCharacterCount.tsx @@ -1,14 +1,22 @@ import { Box, Stack, Text } from "@sanity/ui"; import { StringInputProps } from "sanity"; -export const StringInputWithCharacterCount = (props: StringInputProps) => { - const characterCount = props.value?.length ?? 0; +type StringInputWithCharacterCountProps = StringInputProps & { + maxCount?: number; +}; + +export const StringInputWithCharacterCount = ({ + maxCount, + ...defaultProps +}: StringInputWithCharacterCountProps) => { + const characterCount = defaultProps.value?.length ?? 0; return ( - {props.renderDefault(props)} + {defaultProps.renderDefault(defaultProps)} - {characterCount} characters + {characterCount} + {maxCount && `/${maxCount}`} characters ); diff --git a/studio/schemas/objects/seo.ts b/studio/schemas/objects/seo.ts index 919b566c7..8c2fd5154 100644 --- a/studio/schemas/objects/seo.ts +++ b/studio/schemas/objects/seo.ts @@ -30,7 +30,8 @@ const seo = defineField({ Rule.max(70).error("A title cannot exceed 70 characters"), ], components: { - input: StringInputWithCharacterCount, + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 70 }), }, }), defineField({ @@ -48,7 +49,8 @@ const seo = defineField({ ), ], components: { - input: StringInputWithCharacterCount, + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 160 }), }, }), defineField({ From af923e79bc05416b80bf7207dec5e485af33cb0b Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Wed, 11 Sep 2024 08:04:25 +0200 Subject: [PATCH 3/6] feat(StringInputWithCharacterCount): use character count input for more string fields --- studio/schemas/builders/pageBuilder.ts | 5 +++ studio/schemas/documents/blog.ts | 15 ++++++++- studio/schemas/documents/companyInfo.ts | 6 ++++ studio/schemas/documents/companyLocation.ts | 6 ++++ studio/schemas/fields/categories.ts | 7 +++- studio/schemas/fields/media.ts | 34 ++++++++++---------- studio/schemas/fields/text.ts | 8 +++++ studio/schemas/objects/footerSection.ts | 12 +++++-- studio/schemas/objects/link.ts | 8 ++++- studio/schemas/objects/sections/article.ts | 8 ++++- studio/schemas/objects/sections/hero.ts | 7 +++- studio/schemas/objects/sections/logoSalad.ts | 11 +++++-- studio/schemas/objects/seo.ts | 5 ++- studio/schemas/objects/testimony.ts | 7 ++++ 14 files changed, 112 insertions(+), 27 deletions(-) diff --git a/studio/schemas/builders/pageBuilder.ts b/studio/schemas/builders/pageBuilder.ts index 0384ae7e0..8fc82c4d9 100644 --- a/studio/schemas/builders/pageBuilder.ts +++ b/studio/schemas/builders/pageBuilder.ts @@ -11,6 +11,7 @@ import testimonals from "../objects/sections/testimonials"; import imageSection from "../objects/sections/image"; import grid from "../objects/sections/grid"; import contactForm from "../objects/sections/form"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; export const pageBuilderID = "pageBuilder"; @@ -26,6 +27,10 @@ const pageBuilder = defineType({ "Enter a distinctive name for the dynamic page to help content editors easily identify and manage it. This name is used internally and is not visible on your website.", type: "string", validation: (Rule) => Rule.required().max(30), + components: { + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 30 }), + }, }), pageSlug, seo, diff --git a/studio/schemas/documents/blog.ts b/studio/schemas/documents/blog.ts index fba1bb82b..4d74eadba 100644 --- a/studio/schemas/documents/blog.ts +++ b/studio/schemas/documents/blog.ts @@ -2,6 +2,7 @@ import { defineField, defineType } from "sanity"; import seo from "../objects/seo"; import { pageSlug } from "../schemaTypes/slug"; import { title } from "../fields/text"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; export const blogId = "blog"; @@ -17,6 +18,10 @@ const blog = defineType({ "Enter a distinctive name for the page to help content editors easily identify and manage it. This name is used internally and is not visible on your website.", type: "string", validation: (Rule) => Rule.required().max(30), + components: { + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 30 }), + }, }), pageSlug, seo, @@ -28,7 +33,11 @@ const blog = defineType({ "Enter the label used to refer to all posts regardless of their category. This label will be displayed in the filter section on the main blog page. Examples include 'news', 'stories', or 'posts'.", type: "string", initialValue: "All posts", - validation: (Rule) => Rule.required(), + validation: (Rule) => Rule.required().max(20), + components: { + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 20 }), + }, }), defineField({ name: "categories", @@ -49,6 +58,10 @@ const blog = defineType({ "The name of the category. This will be displayed on the website and used for organizing blog posts.", type: "string", validation: (Rule) => Rule.required().min(1).max(100), + components: { + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 100 }), + }, }), ], }), diff --git a/studio/schemas/documents/companyInfo.ts b/studio/schemas/documents/companyInfo.ts index ec44f7817..bd690a448 100644 --- a/studio/schemas/documents/companyInfo.ts +++ b/studio/schemas/documents/companyInfo.ts @@ -1,5 +1,6 @@ import { defineType, defineField } from "sanity"; import seo from "../objects/seo"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; export const companyInfoID = "companyInfo"; @@ -24,6 +25,11 @@ const companyInfo = defineType({ type: "string", title: "Site Name", description: "The name of your website.", + validation: (rule) => rule.max(60), + components: { + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 60 }), + }, }), defineField({ name: "defaultLanguage", diff --git a/studio/schemas/documents/companyLocation.ts b/studio/schemas/documents/companyLocation.ts index 91352c7c7..47a01f569 100644 --- a/studio/schemas/documents/companyLocation.ts +++ b/studio/schemas/documents/companyLocation.ts @@ -1,4 +1,5 @@ import { defineField, defineType } from "sanity"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; export const companyLocationID = "companyLocation"; export const companyLocationNameID = "companyLocationName"; @@ -13,6 +14,11 @@ const companyLocation = defineType({ name: companyLocationNameID, type: "string", title: "Location", + validation: (rule) => rule.max(50), + components: { + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 50 }), + }, }), ], }); diff --git a/studio/schemas/fields/categories.ts b/studio/schemas/fields/categories.ts index ea7c07d9a..0fc2f6d6e 100644 --- a/studio/schemas/fields/categories.ts +++ b/studio/schemas/fields/categories.ts @@ -1,4 +1,5 @@ -import { defineField } from "sanity"; +import { defineField, StringInputProps } from "sanity"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; export const categoriesId = "categories"; @@ -12,6 +13,10 @@ const categories = defineField({ type: "string", title: "Category", validation: (Rule) => Rule.required().min(1).max(100), + components: { + input: (props: StringInputProps) => + StringInputWithCharacterCount({ ...props, maxCount: 100 }), + }, }, ], }); diff --git a/studio/schemas/fields/media.ts b/studio/schemas/fields/media.ts index e1e7fa403..86bc8b0f9 100644 --- a/studio/schemas/fields/media.ts +++ b/studio/schemas/fields/media.ts @@ -1,4 +1,5 @@ -import { defineField } from "sanity"; +import { defineField, StringInputProps } from "sanity"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; export enum ImageAlignment { Left = "left", @@ -15,20 +16,25 @@ const alignmentOptions = [ { title: "Right", value: ImageAlignment.Right }, ]; +const imageAltField = defineField({ + name: "alt", + type: "string", + title: "Alternative Text", + description: + "Provide a description of the image for accessibility. Leave empty if the image is purely decorative.", + validation: (rule) => rule.max(100), + components: { + input: (props: StringInputProps) => + StringInputWithCharacterCount({ ...props, maxCount: 100 }), + }, +}); + const image = defineField({ name: "image", title: "Image", type: "image", options: { hotspot: true }, - fields: [ - { - name: "alt", - type: "string", - title: "Alternative Text", - description: - "Provide a description of the image for accessibility. Leave empty if the image is purely decorative.", - }, - ], + fields: [imageAltField], }); export const imageExtended = defineField({ @@ -37,13 +43,7 @@ export const imageExtended = defineField({ type: "image", options: { hotspot: true }, fields: [ - { - name: "alt", - type: "string", - title: "Alternative Text", - description: - "Provide a description of the image for accessibility. Leave empty if the image is purely decorative.", - }, + imageAltField, { name: "imageAlignment", title: "Image Alignment", diff --git a/studio/schemas/fields/text.ts b/studio/schemas/fields/text.ts index d0df65def..449387a60 100644 --- a/studio/schemas/fields/text.ts +++ b/studio/schemas/fields/text.ts @@ -1,4 +1,5 @@ import { StringRule, defineField } from "sanity"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; enum titleID { basic = "basicTitle", @@ -35,6 +36,10 @@ const createField = ({ title, type: "string", validation: validationRules, + components: { + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: maxLength }), + }, }); }; @@ -54,6 +59,9 @@ export const optionalSubtitle = defineField({ title: "Subtitle", type: "string", validation: (Rule) => Rule.max(60), + components: { + input: (props) => StringInputWithCharacterCount({ ...props, maxCount: 60 }), + }, }); export const richTextID = "richText"; diff --git a/studio/schemas/objects/footerSection.ts b/studio/schemas/objects/footerSection.ts index 57d7aab7e..fd10a9a76 100644 --- a/studio/schemas/objects/footerSection.ts +++ b/studio/schemas/objects/footerSection.ts @@ -1,7 +1,8 @@ -import { defineType } from "sanity"; +import { defineType, StringInputProps } from "sanity"; import { linkID } from "./link"; import { soMeLinksID } from "../documents/socialMediaProfiles"; import { richText, richTextID } from "../fields/text"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; export const footerSectionID = { main: "footerSection", @@ -32,7 +33,14 @@ export const footerSection = defineType({ type: "string", description: "Enter the title for this footer section. This will help identify the section within the footer.", - validation: (Rule) => Rule.required().error("Section title is required"), + validation: (Rule) => [ + Rule.required().error("Section title is required"), + Rule.max(60), + ], + components: { + input: (props: StringInputProps) => + StringInputWithCharacterCount({ ...props, maxCount: 60 }), + }, }, { name: footerSectionID.type, diff --git a/studio/schemas/objects/link.ts b/studio/schemas/objects/link.ts index 3ed110cd0..32411039e 100644 --- a/studio/schemas/objects/link.ts +++ b/studio/schemas/objects/link.ts @@ -1,7 +1,8 @@ -import { defineField } from "sanity"; +import { defineField, StringInputProps } from "sanity"; import AnchorSelect from "../../components/AnchorSelect"; import LinkTypeSelector from "../../components/LinkTypeSelector"; import NewTabSelector from "../../components/NewTabSelector"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; export const linkID = "link"; @@ -34,6 +35,11 @@ export const link = defineField({ title: "Provide a link title", type: "string", description: "Enter the link text that will be displayed on the website.", + validation: (rule) => rule.max(60), + components: { + input: (props: StringInputProps) => + StringInputWithCharacterCount({ ...props, maxCount: 60 }), + }, }, { name: "linkType", diff --git a/studio/schemas/objects/sections/article.ts b/studio/schemas/objects/sections/article.ts index 69bdcc294..80122a44c 100644 --- a/studio/schemas/objects/sections/article.ts +++ b/studio/schemas/objects/sections/article.ts @@ -1,7 +1,8 @@ -import { defineField } from "sanity"; +import { defineField, StringInputProps } from "sanity"; import { richText, title } from "../../../schemas/fields/text"; import { imageExtended } from "../../../schemas/fields/media"; import { link } from "../link"; +import { StringInputWithCharacterCount } from "../../../components/StringInputWithCharacterCount"; export const articleID = "article"; @@ -14,6 +15,11 @@ export const article = defineField({ name: "tag", title: "Tag", type: "string", + validation: (rule) => rule.max(60), + components: { + input: (props: StringInputProps) => + StringInputWithCharacterCount({ ...props, maxCount: 60 }), + }, }, title, richText, diff --git a/studio/schemas/objects/sections/hero.ts b/studio/schemas/objects/sections/hero.ts index c21fed68f..449ff919f 100644 --- a/studio/schemas/objects/sections/hero.ts +++ b/studio/schemas/objects/sections/hero.ts @@ -1,8 +1,9 @@ // hero.ts -import { defineField } from "sanity"; +import { defineField, StringInputProps } from "sanity"; import callToActionField from "../../fields/callToActionFields"; import CustomCallToActions from "../../../components/CustomCallToActions"; import { title } from "studio/schemas/fields/text"; +import { StringInputWithCharacterCount } from "../../../components/StringInputWithCharacterCount"; export const heroID = "hero"; @@ -17,6 +18,10 @@ export const hero = defineField({ title: "Description", type: "string", validation: (Rule) => Rule.max(200), + components: { + input: (props: StringInputProps) => + StringInputWithCharacterCount({ ...props, maxCount: 200 }), + }, }, { name: "callToActions", diff --git a/studio/schemas/objects/sections/logoSalad.ts b/studio/schemas/objects/sections/logoSalad.ts index 4c04e22b0..83d790b87 100644 --- a/studio/schemas/objects/sections/logoSalad.ts +++ b/studio/schemas/objects/sections/logoSalad.ts @@ -1,6 +1,7 @@ -import { defineField } from "sanity"; +import { defineField, StringInputProps } from "sanity"; import image from "studio/schemas/fields/media"; import { richText } from "studio/schemas/fields/text"; +import { StringInputWithCharacterCount } from "../../../components/StringInputWithCharacterCount"; export const logoSaladID = "logoSalad"; @@ -21,8 +22,14 @@ export const logoSalad = defineField({ type: "string", description: "Required text displayed in a smaller body text style. Use it to provide additional context or details about the logos.", - validation: (Rule) => + validation: (Rule) => [ Rule.required().error("Logo description is required."), + Rule.max(100), + ], + components: { + input: (props: StringInputProps) => + StringInputWithCharacterCount({ ...props, maxCount: 100 }), + }, }, { name: "logos", diff --git a/studio/schemas/objects/seo.ts b/studio/schemas/objects/seo.ts index 8c2fd5154..cf6adaf5b 100644 --- a/studio/schemas/objects/seo.ts +++ b/studio/schemas/objects/seo.ts @@ -1,4 +1,4 @@ -import { defineField } from "sanity"; +import { defineField, StringInputProps } from "sanity"; import { StringInputWithCharacterCount } from "studio/components/StringInputWithCharacterCount"; const seoFieldID = { @@ -59,6 +59,9 @@ const seo = defineField({ title: "SEO & Social Media Keywords", description: "Enter targeted keywords to enhance your content’s visibility in search engines and social media platforms. Use relevant and specific keywords that describe your content, helping to attract the right audience and improve your SEO performance", + components: { + input: StringInputWithCharacterCount, + }, }), defineField({ name: seoFieldID.image, diff --git a/studio/schemas/objects/testimony.ts b/studio/schemas/objects/testimony.ts index ac16166a1..97d3ad022 100644 --- a/studio/schemas/objects/testimony.ts +++ b/studio/schemas/objects/testimony.ts @@ -1,5 +1,7 @@ import { richText, title } from "../fields/text"; import image from "../fields/media"; +import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputProps, StringRule } from "sanity"; export const testimony = { name: "testimony", @@ -11,6 +13,11 @@ export const testimony = { name: "subTitle", type: "string", title: "Subtitle", + validation: (rule: StringRule) => rule.max(100), + components: { + input: (props: StringInputProps) => + StringInputWithCharacterCount({ ...props, maxCount: 100 }), + }, }, image, richText, From 57395c2967dac6973ce73ca6f5b277043dc1c24c Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Thu, 12 Sep 2024 13:46:29 +0200 Subject: [PATCH 4/6] =?UTF-8?q?refactor(schema):=20rename=20Rule=20?= =?UTF-8?q?=E2=86=92=20rule=20in=20validation=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- studio/schemas/builders/pageBuilder.ts | 2 +- studio/schemas/documents/blog.ts | 6 +-- studio/schemas/documents/navigationManager.ts | 4 +- studio/schemas/documents/post.ts | 12 ++--- studio/schemas/fields/categories.ts | 2 +- studio/schemas/fields/text.ts | 6 +-- .../compensations/benefitsByLocation.ts | 8 ++-- .../compensations/bonusesByLocation.ts | 13 ++--- .../schemas/objects/compensations/pension.ts | 7 +-- .../compensations/salariesByLocation.ts | 8 ++-- studio/schemas/objects/footerSection.ts | 8 ++-- studio/schemas/objects/link.ts | 48 ++++++++++--------- .../schemas/objects/sections/callToAction.ts | 2 +- studio/schemas/objects/sections/form.ts | 5 +- studio/schemas/objects/sections/grid.ts | 8 ++-- studio/schemas/objects/sections/hero.ts | 6 +-- studio/schemas/objects/sections/image.ts | 2 +- studio/schemas/objects/sections/logoSalad.ts | 11 +++-- .../schemas/objects/sections/testimonials.ts | 5 +- studio/schemas/objects/seo.ts | 25 ++++++---- studio/schemas/objects/socialMedia.ts | 4 +- studio/schemas/schemaTypes/slug.ts | 4 +- studioShared/schemas/documents/blogPosts.ts | 4 +- 23 files changed, 106 insertions(+), 94 deletions(-) diff --git a/studio/schemas/builders/pageBuilder.ts b/studio/schemas/builders/pageBuilder.ts index 8fc82c4d9..d86c0c371 100644 --- a/studio/schemas/builders/pageBuilder.ts +++ b/studio/schemas/builders/pageBuilder.ts @@ -26,7 +26,7 @@ const pageBuilder = defineType({ description: "Enter a distinctive name for the dynamic page to help content editors easily identify and manage it. This name is used internally and is not visible on your website.", type: "string", - validation: (Rule) => Rule.required().max(30), + validation: (rule) => rule.required().max(30), components: { input: (props) => StringInputWithCharacterCount({ ...props, maxCount: 30 }), diff --git a/studio/schemas/documents/blog.ts b/studio/schemas/documents/blog.ts index 4d74eadba..18a5cd2ec 100644 --- a/studio/schemas/documents/blog.ts +++ b/studio/schemas/documents/blog.ts @@ -17,7 +17,7 @@ const blog = defineType({ description: "Enter a distinctive name for the page to help content editors easily identify and manage it. This name is used internally and is not visible on your website.", type: "string", - validation: (Rule) => Rule.required().max(30), + validation: (rule) => rule.required().max(30), components: { input: (props) => StringInputWithCharacterCount({ ...props, maxCount: 30 }), @@ -33,7 +33,7 @@ const blog = defineType({ "Enter the label used to refer to all posts regardless of their category. This label will be displayed in the filter section on the main blog page. Examples include 'news', 'stories', or 'posts'.", type: "string", initialValue: "All posts", - validation: (Rule) => Rule.required().max(20), + validation: (rule) => rule.required().max(20), components: { input: (props) => StringInputWithCharacterCount({ ...props, maxCount: 20 }), @@ -57,7 +57,7 @@ const blog = defineType({ description: "The name of the category. This will be displayed on the website and used for organizing blog posts.", type: "string", - validation: (Rule) => Rule.required().min(1).max(100), + validation: (rule) => rule.required().min(1).max(100), components: { input: (props) => StringInputWithCharacterCount({ ...props, maxCount: 100 }), diff --git a/studio/schemas/documents/navigationManager.ts b/studio/schemas/documents/navigationManager.ts index 24f5c3560..817e2cc83 100644 --- a/studio/schemas/documents/navigationManager.ts +++ b/studio/schemas/documents/navigationManager.ts @@ -36,8 +36,8 @@ const navigationManager = defineType({ "Add links to the main menu. These links will appear at the top of your website and help visitors navigate to important sections. The first Call to Action (CTA) will be styled as a primary link button. Note: The order in which you add the links here is how they will be displayed on the website.", type: "array", of: [{ type: linkID }, { type: callToActionFieldID }], - validation: (Rule) => - Rule.custom((links) => { + validation: (rule) => + rule.custom((links) => { if (!Array.isArray(links)) return true; const ctaCount = links.filter( (link) => link._type === callToActionFieldID, diff --git a/studio/schemas/documents/post.ts b/studio/schemas/documents/post.ts index 78ddf0854..62ad9690f 100644 --- a/studio/schemas/documents/post.ts +++ b/studio/schemas/documents/post.ts @@ -20,8 +20,8 @@ const posts = defineType({ description: "Select the date and time when this post will be published.", type: "datetime", initialValue: () => new Date().toISOString(), - validation: (Rule) => - Rule.required().custom((date, context) => { + validation: (rule) => + rule.required().custom((date, context) => { // Ensure date is not undefined or null if (!date) return "The publish date is required."; @@ -57,7 +57,7 @@ const posts = defineType({ components: { input: CategorySelector, }, - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }), defineField({ name: "lead", @@ -69,15 +69,15 @@ const posts = defineType({ defineField({ ...richText, description: "Enter the introductory text for the post.", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }), defineField({ ...image, description: "Upload a featured image for the post.", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }), ], - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }), defineField({ ...richText, diff --git a/studio/schemas/fields/categories.ts b/studio/schemas/fields/categories.ts index 0fc2f6d6e..bb286fbb0 100644 --- a/studio/schemas/fields/categories.ts +++ b/studio/schemas/fields/categories.ts @@ -12,7 +12,7 @@ const categories = defineField({ name: "category", type: "string", title: "Category", - validation: (Rule) => Rule.required().min(1).max(100), + validation: (rule) => rule.required().min(1).max(100), components: { input: (props: StringInputProps) => StringInputWithCharacterCount({ ...props, maxCount: 100 }), diff --git a/studio/schemas/fields/text.ts b/studio/schemas/fields/text.ts index 449387a60..b120b6ddb 100644 --- a/studio/schemas/fields/text.ts +++ b/studio/schemas/fields/text.ts @@ -23,8 +23,8 @@ const createField = ({ isRequired = false, maxLength = 60, }: CreateFieldProps) => { - const validationRules = (Rule: StringRule) => { - let rules = Rule.max(maxLength); + const validationRules = (rule: StringRule) => { + let rules = rule.max(maxLength); if (isRequired) { rules = rules.required(); } @@ -58,7 +58,7 @@ export const optionalSubtitle = defineField({ name: subtitleID.optional, title: "Subtitle", type: "string", - validation: (Rule) => Rule.max(60), + validation: (rule) => rule.max(60), components: { input: (props) => StringInputWithCharacterCount({ ...props, maxCount: 60 }), }, diff --git a/studio/schemas/objects/compensations/benefitsByLocation.ts b/studio/schemas/objects/compensations/benefitsByLocation.ts index 991d8681a..dde5db400 100644 --- a/studio/schemas/objects/compensations/benefitsByLocation.ts +++ b/studio/schemas/objects/compensations/benefitsByLocation.ts @@ -28,7 +28,7 @@ const benefitType = defineField({ layout: BENEFIT_TYPES.length > 5 ? "dropdown" : "radio", }, initialValue: BENEFIT_TYPE_BASIC_VALUE, - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }); export const benefitsByLocation = defineField({ @@ -45,7 +45,7 @@ export const benefitsByLocation = defineField({ ...location, description: "Select the office location for which you are entering benefits information. Each location must be unique.", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }, defineField({ name: "benefits", @@ -93,8 +93,8 @@ export const benefitsByLocation = defineField({ }, }, ], - validation: (Rule) => - Rule.custom((benefitsByLocation) => { + validation: (rule) => + rule.custom((benefitsByLocation) => { const isNotDuplicate: boolean = checkForDuplicateLocations( benefitsByLocation as DocumentWithLocation[] | undefined, ); diff --git a/studio/schemas/objects/compensations/bonusesByLocation.ts b/studio/schemas/objects/compensations/bonusesByLocation.ts index d71c64847..4228f917d 100644 --- a/studio/schemas/objects/compensations/bonusesByLocation.ts +++ b/studio/schemas/objects/compensations/bonusesByLocation.ts @@ -25,7 +25,7 @@ export const bonusesByLocation = defineField({ ...location, description: "Select the company location for which you are entering the yearly bonus data. Each location must be unique.", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }, defineField({ name: "yearlyBonuses", @@ -43,7 +43,7 @@ export const bonusesByLocation = defineField({ description: "The calendar year for which this bonus was given", type: "number", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }), defineField({ name: "bonus", @@ -51,8 +51,9 @@ export const bonusesByLocation = defineField({ description: "Enter the bonus amount for this year. Ensure the amount is positive and reflective of the compensation package for this location.", type: "number", - validation: (Rule) => - Rule.required() + validation: (rule) => + rule + .required() .min(0) .error("Please enter a positive bonus amount."), }), @@ -95,8 +96,8 @@ export const bonusesByLocation = defineField({ }, }), ], - validation: (Rule) => - Rule.custom((bonusesByLocation) => { + validation: (rule) => + rule.custom((bonusesByLocation) => { const isNotDuplicate: boolean = checkForDuplicateLocations( bonusesByLocation as DocumentWithLocation[] | undefined, ); diff --git a/studio/schemas/objects/compensations/pension.ts b/studio/schemas/objects/compensations/pension.ts index bdaeefa30..86685334a 100644 --- a/studio/schemas/objects/compensations/pension.ts +++ b/studio/schemas/objects/compensations/pension.ts @@ -6,11 +6,12 @@ export const pension = defineField({ type: "number", initialValue: 7, description: `Specify the percentage of the pension provided by Variant for employees. The value should be a positive number and will be used to calculate the pension amount.`, - validation: (Rule) => [ - Rule.min(0) + validation: (rule) => [ + rule + .min(0) .max(100) .error("The pension percentage must be a number between 0 and 100."), - Rule.custom((value, context) => { + rule.custom((value, context) => { if ( context.document?.showSalaryCalculator && (value === undefined || value === null) diff --git a/studio/schemas/objects/compensations/salariesByLocation.ts b/studio/schemas/objects/compensations/salariesByLocation.ts index 134bbd55c..a7aa68c07 100644 --- a/studio/schemas/objects/compensations/salariesByLocation.ts +++ b/studio/schemas/objects/compensations/salariesByLocation.ts @@ -24,7 +24,7 @@ export const salariesByLocation = defineField({ ...location, description: "Select the company location for which you are entering the salary information. Each location must be unique.", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }, defineField({ name: "yearlySalaries", @@ -42,7 +42,7 @@ export const salariesByLocation = defineField({ description: "The calendar year for which these salaries were in effect", type: "number", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }), defineField({ name: "salaries", @@ -87,8 +87,8 @@ export const salariesByLocation = defineField({ }, }, ], - validation: (Rule) => - Rule.custom((salariesByLocation) => { + validation: (rule) => + rule.custom((salariesByLocation) => { const isNotDuplicate: boolean = checkForDuplicateLocations( salariesByLocation as DocumentWithLocation[] | undefined, ); diff --git a/studio/schemas/objects/footerSection.ts b/studio/schemas/objects/footerSection.ts index fd10a9a76..046affb5b 100644 --- a/studio/schemas/objects/footerSection.ts +++ b/studio/schemas/objects/footerSection.ts @@ -33,9 +33,9 @@ export const footerSection = defineType({ type: "string", description: "Enter the title for this footer section. This will help identify the section within the footer.", - validation: (Rule) => [ - Rule.required().error("Section title is required"), - Rule.max(60), + validation: (rule) => [ + rule.required().error("Section title is required"), + rule.max(60), ], components: { input: (props: StringInputProps) => @@ -55,7 +55,7 @@ export const footerSection = defineType({ ], layout: "dropdown", }, - validation: (Rule) => Rule.required().error("Content type is required"), + validation: (rule) => rule.required().error("Content type is required"), initialValue: SectionType.Content, }, { diff --git a/studio/schemas/objects/link.ts b/studio/schemas/objects/link.ts index 32411039e..8f828ccc5 100644 --- a/studio/schemas/objects/link.ts +++ b/studio/schemas/objects/link.ts @@ -50,8 +50,8 @@ export const link = defineField({ components: { input: LinkTypeSelector, }, - validation: (Rule) => - Rule.custom((value, context) => { + validation: (rule) => + rule.custom((value, context) => { const parent = context.parent as Parent; if (parent?.linkTitle && !value) { return "Link type is required"; @@ -69,8 +69,8 @@ export const link = defineField({ { type: lazyBlogID() }, { type: lazyCompensationsID() }, ], - validation: (Rule: any) => - Rule.custom((value: any, context: any) => { + validation: (rule) => + rule.custom((value: any, context: any) => { const parent = context.parent as Parent; if ( parent?.linkTitle && @@ -93,29 +93,31 @@ export const link = defineField({ type: "url", description: "Enter the full URL for the external link, including 'https://'. For example, 'https://www.example.com'.", - validation: (Rule) => - Rule.uri({ - scheme: ["http", "https"], - allowRelative: false, - }).custom((value, context) => { - const parent = context.parent as Parent; - if ( - parent?.linkTitle && - parent?.linkType === LinkType.External && - !value - ) { - return "URL is required for external links"; - } - return true; - }), + validation: (rule) => + rule + .uri({ + scheme: ["http", "https"], + allowRelative: false, + }) + .custom((value, context) => { + const parent = context.parent as Parent; + if ( + parent?.linkTitle && + parent?.linkType === LinkType.External && + !value + ) { + return "URL is required for external links"; + } + return true; + }), hidden: ({ parent }) => parent?.linkType !== LinkType.External, }, { name: "email", title: "Enter the email address", type: "string", - validation: (Rule) => - Rule.custom((value: string, context) => { + validation: (rule) => + rule.custom((value: string, context) => { const parent = context.parent as Parent; if ( parent?.linkTitle && @@ -135,8 +137,8 @@ export const link = defineField({ name: "phone", title: "Enter the phone number", type: "string", - validation: (Rule) => - Rule.custom((value: string, context) => { + validation: (rule) => + rule.custom((value: string, context) => { const parent = context.parent as Parent; if ( parent?.linkTitle && diff --git a/studio/schemas/objects/sections/callToAction.ts b/studio/schemas/objects/sections/callToAction.ts index e9a701cfc..7ae645494 100644 --- a/studio/schemas/objects/sections/callToAction.ts +++ b/studio/schemas/objects/sections/callToAction.ts @@ -29,7 +29,7 @@ export const callToAction = defineField({ preview: callToActionField.preview, }, ], - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }, ], initialValue: { diff --git a/studio/schemas/objects/sections/form.ts b/studio/schemas/objects/sections/form.ts index 78554391c..1b0c5c898 100644 --- a/studio/schemas/objects/sections/form.ts +++ b/studio/schemas/objects/sections/form.ts @@ -21,8 +21,9 @@ export const contactForm = defineField({ styles: [{ title: "Normal", value: "normal" }], }, ], - validation: (Rule) => - Rule.required() + validation: (rule) => + rule + .required() .min(1) .error("Consent Agreement Text is required and cannot be empty."), }, diff --git a/studio/schemas/objects/sections/grid.ts b/studio/schemas/objects/sections/grid.ts index d50038e57..4f8916414 100644 --- a/studio/schemas/objects/sections/grid.ts +++ b/studio/schemas/objects/sections/grid.ts @@ -26,7 +26,7 @@ export const grid = defineField({ title: "Item Title", description: "Title of the grid item, such as the name of an employee.", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }, { ...richText, @@ -39,7 +39,7 @@ export const grid = defineField({ title: "Item Image", description: "Image of the grid item, such as a photo of an employee.", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }, ], preview: { @@ -58,8 +58,8 @@ export const grid = defineField({ }, }, ], - validation: (Rule) => - Rule.required().min(1).error("At least one grid item is required."), + validation: (rule) => + rule.required().min(1).error("At least one grid item is required."), }, ], preview: { diff --git a/studio/schemas/objects/sections/hero.ts b/studio/schemas/objects/sections/hero.ts index 449ff919f..e051243b0 100644 --- a/studio/schemas/objects/sections/hero.ts +++ b/studio/schemas/objects/sections/hero.ts @@ -17,7 +17,7 @@ export const hero = defineField({ name: "description", title: "Description", type: "string", - validation: (Rule) => Rule.max(200), + validation: (rule) => rule.max(200), components: { input: (props: StringInputProps) => StringInputWithCharacterCount({ ...props, maxCount: 200 }), @@ -36,8 +36,8 @@ export const hero = defineField({ preview: callToActionField.preview, }, ], - validation: (Rule) => - Rule.custom((callToActions) => { + validation: (rule) => + rule.custom((callToActions) => { if (!Array.isArray(callToActions)) return true; if (callToActions.length > 2) { return "You can only have two Call to Action links"; diff --git a/studio/schemas/objects/sections/image.ts b/studio/schemas/objects/sections/image.ts index bd9158210..dde7bcb15 100644 --- a/studio/schemas/objects/sections/image.ts +++ b/studio/schemas/objects/sections/image.ts @@ -13,7 +13,7 @@ export const imageSection = defineField({ defineField({ ...image, description: "Upload a featured image for the section.", - validation: (Rule) => Rule.required(), + validation: (rule) => rule.required(), }), ], preview: { diff --git a/studio/schemas/objects/sections/logoSalad.ts b/studio/schemas/objects/sections/logoSalad.ts index 83d790b87..7b6a6a900 100644 --- a/studio/schemas/objects/sections/logoSalad.ts +++ b/studio/schemas/objects/sections/logoSalad.ts @@ -22,9 +22,9 @@ export const logoSalad = defineField({ type: "string", description: "Required text displayed in a smaller body text style. Use it to provide additional context or details about the logos.", - validation: (Rule) => [ - Rule.required().error("Logo description is required."), - Rule.max(100), + validation: (rule) => [ + rule.required().error("Logo description is required."), + rule.max(100), ], components: { input: (props: StringInputProps) => @@ -38,8 +38,9 @@ export const logoSalad = defineField({ "Add a list of logos to display. You must include between 6 and 12 logos.", type: "array", of: [image], - validation: (Rule) => - Rule.min(6) + validation: (rule) => + rule + .min(6) .error("At least 6 logos are required.") .max(12) .error("You can add up to 12 logos.") diff --git a/studio/schemas/objects/sections/testimonials.ts b/studio/schemas/objects/sections/testimonials.ts index bbc24b3dd..66cdf48e5 100644 --- a/studio/schemas/objects/sections/testimonials.ts +++ b/studio/schemas/objects/sections/testimonials.ts @@ -15,8 +15,9 @@ export const testimonals = defineField({ title: "List of Testimonials", type: "array", of: [testimony], - validation: (Rule) => - Rule.required() + validation: (rule) => + rule + .required() .min(1) .max(4) .error("You must have between 1 and 4 testimonials."), diff --git a/studio/schemas/objects/seo.ts b/studio/schemas/objects/seo.ts index cf6adaf5b..44f1f9acc 100644 --- a/studio/schemas/objects/seo.ts +++ b/studio/schemas/objects/seo.ts @@ -23,11 +23,12 @@ const seo = defineField({ title: "SEO & Social Media Title", description: "Create an engaging title that attracts users on social media and in search results. Keep the title between 15-70 characters for the best results.", - validation: (Rule) => [ - Rule.required() + validation: (rule) => [ + rule + .required() .min(15) .error("A title of minimum 15 characters is required"), - Rule.max(70).error("A title cannot exceed 70 characters"), + rule.max(70), ], components: { input: (props) => @@ -40,13 +41,17 @@ const seo = defineField({ title: "SEO & Social Media Description", description: "An optional but recommended short description to boost visitor engagement from social media and search engines. Try to keep it between 70-160 characters.", - validation: (Rule) => [ - Rule.min(70).warning( - "A description of at least 70 characters has a higher chance of converting visitors", - ), - Rule.max(160).warning( - "A description of more than 160 characters has a lower chance of converting visitors", - ), + validation: (rule) => [ + rule + .min(70) + .warning( + "A description of at least 70 characters has a higher chance of converting visitors", + ), + rule + .max(160) + .warning( + "A description of more than 160 characters has a lower chance of converting visitors", + ), ], components: { input: (props) => diff --git a/studio/schemas/objects/socialMedia.ts b/studio/schemas/objects/socialMedia.ts index 5d9943b58..70cbe1a1e 100644 --- a/studio/schemas/objects/socialMedia.ts +++ b/studio/schemas/objects/socialMedia.ts @@ -19,8 +19,8 @@ export const socialMedia = defineType({ name: SocialMediaID.url, type: "url", title: "URL", - validation: (Rule) => - Rule.uri({ + validation: (rule) => + rule.uri({ allowRelative: false, scheme: ["http", "https"], }), diff --git a/studio/schemas/schemaTypes/slug.ts b/studio/schemas/schemaTypes/slug.ts index c18745250..602211222 100644 --- a/studio/schemas/schemaTypes/slug.ts +++ b/studio/schemas/schemaTypes/slug.ts @@ -47,8 +47,8 @@ function createSlugField(source: string) { .slice(0, SLUG_MAX_LENGTH), isUnique: isSlugUniqueAcrossAllDocuments, }, - validation: (Rule) => - Rule.required().custom((value) => { + validation: (rule) => + rule.required().custom((value) => { if (value?.current === undefined) return true; return ( encodeURIComponent(value.current) === value.current || diff --git a/studioShared/schemas/documents/blogPosts.ts b/studioShared/schemas/documents/blogPosts.ts index 10208b062..0a31ead1b 100644 --- a/studioShared/schemas/documents/blogPosts.ts +++ b/studioShared/schemas/documents/blogPosts.ts @@ -18,8 +18,8 @@ const blogPosts = defineType({ description: "Select the date and time when this post will be published.", type: "datetime", initialValue: () => new Date().toISOString(), - validation: (Rule) => - Rule.required().custom((date, context) => { + validation: (rule) => + rule.required().custom((date, context) => { // Ensure date is not undefined or null if (!date) return "The publish date is required."; From 996773ea0bb903003058fe6ae0ab7b0359af4df2 Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Wed, 11 Sep 2024 08:28:19 +0200 Subject: [PATCH 5/6] feat(StringInputWithCharacterCount): handle plural form --- studio/components/StringInputWithCharacterCount.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/studio/components/StringInputWithCharacterCount.tsx b/studio/components/StringInputWithCharacterCount.tsx index 852581088..fa674376e 100644 --- a/studio/components/StringInputWithCharacterCount.tsx +++ b/studio/components/StringInputWithCharacterCount.tsx @@ -10,13 +10,16 @@ export const StringInputWithCharacterCount = ({ ...defaultProps }: StringInputWithCharacterCountProps) => { const characterCount = defaultProps.value?.length ?? 0; + const isPlural = + (maxCount !== undefined && maxCount !== 1) || + (maxCount === undefined && characterCount !== 1); return ( {defaultProps.renderDefault(defaultProps)} {characterCount} - {maxCount && `/${maxCount}`} characters + {maxCount && `/${maxCount}`} character{isPlural ? "s" : ""} ); From 79bc1134617fcde219233dacd458fbfc4fcd47bf Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Fri, 13 Sep 2024 08:57:14 +0200 Subject: [PATCH 6/6] feat(StringInputWithCharacterCount): make maxCount required and show characters left --- .../StringInputWithCharacterCount.tsx | 26 ----------- .../StringInputWithCharacterCount.tsx | 44 +++++++++++++++++++ .../stringInputWithCharacterCount.module.css | 13 ++++++ studio/schemas/builders/pageBuilder.ts | 2 +- studio/schemas/documents/blog.ts | 2 +- studio/schemas/documents/companyInfo.ts | 2 +- studio/schemas/documents/companyLocation.ts | 2 +- studio/schemas/fields/categories.ts | 2 +- studio/schemas/fields/media.ts | 2 +- studio/schemas/fields/text.ts | 2 +- studio/schemas/objects/footerSection.ts | 2 +- studio/schemas/objects/link.ts | 2 +- studio/schemas/objects/sections/article.ts | 2 +- studio/schemas/objects/sections/hero.ts | 2 +- studio/schemas/objects/sections/logoSalad.ts | 2 +- studio/schemas/objects/seo.ts | 5 ++- studio/schemas/objects/testimony.ts | 2 +- 17 files changed, 73 insertions(+), 41 deletions(-) delete mode 100644 studio/components/StringInputWithCharacterCount.tsx create mode 100644 studio/components/stringInputWithCharacterCount/StringInputWithCharacterCount.tsx create mode 100644 studio/components/stringInputWithCharacterCount/stringInputWithCharacterCount.module.css diff --git a/studio/components/StringInputWithCharacterCount.tsx b/studio/components/StringInputWithCharacterCount.tsx deleted file mode 100644 index fa674376e..000000000 --- a/studio/components/StringInputWithCharacterCount.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Box, Stack, Text } from "@sanity/ui"; -import { StringInputProps } from "sanity"; - -type StringInputWithCharacterCountProps = StringInputProps & { - maxCount?: number; -}; - -export const StringInputWithCharacterCount = ({ - maxCount, - ...defaultProps -}: StringInputWithCharacterCountProps) => { - const characterCount = defaultProps.value?.length ?? 0; - const isPlural = - (maxCount !== undefined && maxCount !== 1) || - (maxCount === undefined && characterCount !== 1); - - return ( - - {defaultProps.renderDefault(defaultProps)} - - {characterCount} - {maxCount && `/${maxCount}`} character{isPlural ? "s" : ""} - - - ); -}; diff --git a/studio/components/stringInputWithCharacterCount/StringInputWithCharacterCount.tsx b/studio/components/stringInputWithCharacterCount/StringInputWithCharacterCount.tsx new file mode 100644 index 000000000..9738b3e77 --- /dev/null +++ b/studio/components/stringInputWithCharacterCount/StringInputWithCharacterCount.tsx @@ -0,0 +1,44 @@ +import { Box, Stack, Text } from "@sanity/ui"; +import { StringInputProps } from "sanity"; +import styles from "./stringInputWithCharacterCount.module.css"; + +type StringInputWithCharacterCountProps = StringInputProps & { + maxCount: number; +}; + +const SR_COUNT_HINT_ID = "string-input-with-character-count-input-info"; + +export const StringInputWithCharacterCount = ({ + maxCount, + ...defaultProps +}: StringInputWithCharacterCountProps) => { + const characterCount = defaultProps.value?.length ?? 0; + const charactersLeft = maxCount - characterCount; + const isOverLimit = charactersLeft < 0; + const countText = `${Math.abs(charactersLeft)} character${charactersLeft !== 1 ? "s" : ""} ${isOverLimit ? "over limit" : charactersLeft === maxCount ? "allowed" : "left"}`; + + return ( + + + {defaultProps.renderDefault({ + ...defaultProps, + elementProps: { + ...defaultProps.elementProps, + "aria-describedby": `${defaultProps.elementProps["aria-describedby"]} ${SR_COUNT_HINT_ID}`, + }, + })} + + + {`You can enter up to ${maxCount} character${charactersLeft !== 1 ? "s" : ""}`} + + + {countText} + + + ); +}; diff --git a/studio/components/stringInputWithCharacterCount/stringInputWithCharacterCount.module.css b/studio/components/stringInputWithCharacterCount/stringInputWithCharacterCount.module.css new file mode 100644 index 000000000..21c22f33d --- /dev/null +++ b/studio/components/stringInputWithCharacterCount/stringInputWithCharacterCount.module.css @@ -0,0 +1,13 @@ +.srOnly { + /* visually hidden input to still receive focus and support screen readers */ + /* https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications#using_a_label_element_to_trigger_a_hidden_file_input_element */ + position: absolute !important; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px); +} + +.overLimit { + color: rgb(203, 38, 24) !important; +} diff --git a/studio/schemas/builders/pageBuilder.ts b/studio/schemas/builders/pageBuilder.ts index d86c0c371..1bfc7748f 100644 --- a/studio/schemas/builders/pageBuilder.ts +++ b/studio/schemas/builders/pageBuilder.ts @@ -11,7 +11,7 @@ import testimonals from "../objects/sections/testimonials"; import imageSection from "../objects/sections/image"; import grid from "../objects/sections/grid"; import contactForm from "../objects/sections/form"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const pageBuilderID = "pageBuilder"; diff --git a/studio/schemas/documents/blog.ts b/studio/schemas/documents/blog.ts index 18a5cd2ec..6873609fd 100644 --- a/studio/schemas/documents/blog.ts +++ b/studio/schemas/documents/blog.ts @@ -2,7 +2,7 @@ import { defineField, defineType } from "sanity"; import seo from "../objects/seo"; import { pageSlug } from "../schemaTypes/slug"; import { title } from "../fields/text"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const blogId = "blog"; diff --git a/studio/schemas/documents/companyInfo.ts b/studio/schemas/documents/companyInfo.ts index bd690a448..7a4233d07 100644 --- a/studio/schemas/documents/companyInfo.ts +++ b/studio/schemas/documents/companyInfo.ts @@ -1,6 +1,6 @@ import { defineType, defineField } from "sanity"; import seo from "../objects/seo"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const companyInfoID = "companyInfo"; diff --git a/studio/schemas/documents/companyLocation.ts b/studio/schemas/documents/companyLocation.ts index 47a01f569..ca2a04ed5 100644 --- a/studio/schemas/documents/companyLocation.ts +++ b/studio/schemas/documents/companyLocation.ts @@ -1,5 +1,5 @@ import { defineField, defineType } from "sanity"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const companyLocationID = "companyLocation"; export const companyLocationNameID = "companyLocationName"; diff --git a/studio/schemas/fields/categories.ts b/studio/schemas/fields/categories.ts index bb286fbb0..d91161216 100644 --- a/studio/schemas/fields/categories.ts +++ b/studio/schemas/fields/categories.ts @@ -1,5 +1,5 @@ import { defineField, StringInputProps } from "sanity"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const categoriesId = "categories"; diff --git a/studio/schemas/fields/media.ts b/studio/schemas/fields/media.ts index 86bc8b0f9..aaf3795e6 100644 --- a/studio/schemas/fields/media.ts +++ b/studio/schemas/fields/media.ts @@ -1,5 +1,5 @@ import { defineField, StringInputProps } from "sanity"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export enum ImageAlignment { Left = "left", diff --git a/studio/schemas/fields/text.ts b/studio/schemas/fields/text.ts index b120b6ddb..3f22fde1f 100644 --- a/studio/schemas/fields/text.ts +++ b/studio/schemas/fields/text.ts @@ -1,5 +1,5 @@ import { StringRule, defineField } from "sanity"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; enum titleID { basic = "basicTitle", diff --git a/studio/schemas/objects/footerSection.ts b/studio/schemas/objects/footerSection.ts index 046affb5b..c3d7e5f17 100644 --- a/studio/schemas/objects/footerSection.ts +++ b/studio/schemas/objects/footerSection.ts @@ -2,7 +2,7 @@ import { defineType, StringInputProps } from "sanity"; import { linkID } from "./link"; import { soMeLinksID } from "../documents/socialMediaProfiles"; import { richText, richTextID } from "../fields/text"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const footerSectionID = { main: "footerSection", diff --git a/studio/schemas/objects/link.ts b/studio/schemas/objects/link.ts index 8f828ccc5..388605aa6 100644 --- a/studio/schemas/objects/link.ts +++ b/studio/schemas/objects/link.ts @@ -2,7 +2,7 @@ import { defineField, StringInputProps } from "sanity"; import AnchorSelect from "../../components/AnchorSelect"; import LinkTypeSelector from "../../components/LinkTypeSelector"; import NewTabSelector from "../../components/NewTabSelector"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const linkID = "link"; diff --git a/studio/schemas/objects/sections/article.ts b/studio/schemas/objects/sections/article.ts index 80122a44c..e22f0d3dd 100644 --- a/studio/schemas/objects/sections/article.ts +++ b/studio/schemas/objects/sections/article.ts @@ -2,7 +2,7 @@ import { defineField, StringInputProps } from "sanity"; import { richText, title } from "../../../schemas/fields/text"; import { imageExtended } from "../../../schemas/fields/media"; import { link } from "../link"; -import { StringInputWithCharacterCount } from "../../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const articleID = "article"; diff --git a/studio/schemas/objects/sections/hero.ts b/studio/schemas/objects/sections/hero.ts index e051243b0..daee8f10c 100644 --- a/studio/schemas/objects/sections/hero.ts +++ b/studio/schemas/objects/sections/hero.ts @@ -3,7 +3,7 @@ import { defineField, StringInputProps } from "sanity"; import callToActionField from "../../fields/callToActionFields"; import CustomCallToActions from "../../../components/CustomCallToActions"; import { title } from "studio/schemas/fields/text"; -import { StringInputWithCharacterCount } from "../../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const heroID = "hero"; diff --git a/studio/schemas/objects/sections/logoSalad.ts b/studio/schemas/objects/sections/logoSalad.ts index 7b6a6a900..5c9314970 100644 --- a/studio/schemas/objects/sections/logoSalad.ts +++ b/studio/schemas/objects/sections/logoSalad.ts @@ -1,7 +1,7 @@ import { defineField, StringInputProps } from "sanity"; import image from "studio/schemas/fields/media"; import { richText } from "studio/schemas/fields/text"; -import { StringInputWithCharacterCount } from "../../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; export const logoSaladID = "logoSalad"; diff --git a/studio/schemas/objects/seo.ts b/studio/schemas/objects/seo.ts index 44f1f9acc..010bd39e3 100644 --- a/studio/schemas/objects/seo.ts +++ b/studio/schemas/objects/seo.ts @@ -1,5 +1,5 @@ import { defineField, StringInputProps } from "sanity"; -import { StringInputWithCharacterCount } from "studio/components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "studio/components/stringInputWithCharacterCount/StringInputWithCharacterCount"; const seoFieldID = { title: "seoTitle", @@ -65,7 +65,8 @@ const seo = defineField({ description: "Enter targeted keywords to enhance your content’s visibility in search engines and social media platforms. Use relevant and specific keywords that describe your content, helping to attract the right audience and improve your SEO performance", components: { - input: StringInputWithCharacterCount, + input: (props) => + StringInputWithCharacterCount({ ...props, maxCount: 200 }), }, }), defineField({ diff --git a/studio/schemas/objects/testimony.ts b/studio/schemas/objects/testimony.ts index 97d3ad022..7d9cbbfa0 100644 --- a/studio/schemas/objects/testimony.ts +++ b/studio/schemas/objects/testimony.ts @@ -1,6 +1,6 @@ import { richText, title } from "../fields/text"; import image from "../fields/media"; -import { StringInputWithCharacterCount } from "../../components/StringInputWithCharacterCount"; +import { StringInputWithCharacterCount } from "../../components/stringInputWithCharacterCount/StringInputWithCharacterCount"; import { StringInputProps, StringRule } from "sanity"; export const testimony = {