From 33935efd06a78785e5689415f603ab5130076cc7 Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Tue, 17 Sep 2024 15:32:55 +0200 Subject: [PATCH] refactor: no explicit any enabled eslint rule no-explicit-any and fixed reported errors --- .eslintrc.cjs | 1 - src/app/(main)/layout.tsx | 2 +- src/app/api/fetchData/route.ts | 2 +- src/components/richText/RichText.tsx | 35 +++++----- src/components/sections/callout/Callout.tsx | 6 +- src/components/sections/grid/Grid.tsx | 6 +- .../sections/logoSalad/LogoSalad.tsx | 6 +- src/post/lead/Lead.tsx | 6 +- src/utils/reactNode.ts | 20 ++++++ src/utils/seo.ts | 5 +- studio/components/AnchorSelect.tsx | 12 +--- studio/components/SoMeInputs.tsx | 26 ++++++- studio/components/slug/ReferenceSlugInput.tsx | 70 ------------------- studio/lib/fetchWithToken.ts | 2 +- studio/schemas/objects/link.ts | 2 +- studio/utils/validations.ts | 13 ---- 16 files changed, 81 insertions(+), 133 deletions(-) create mode 100644 src/utils/reactNode.ts delete mode 100644 studio/components/slug/ReferenceSlugInput.tsx delete mode 100644 studio/utils/validations.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index a6be710a3..b83f65ddd 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -31,7 +31,6 @@ module.exports = { }, }, rules: { - "@typescript-eslint/no-explicit-any": "off", // TODO "unused-imports/no-unused-imports": "error", "import/no-named-as-default": "off", "import/no-unresolved": "error", diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 0b6865141..e28cf5ca2 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -18,7 +18,7 @@ import { loadQuery } from "studio/lib/store"; import styles from "./layout.module.css"; -const hasValidData = (data: any) => data && Object.keys(data).length > 0; +const hasValidData = (data: unknown) => data && Object.keys(data).length > 0; export default async function Layout({ children, diff --git a/src/app/api/fetchData/route.ts b/src/app/api/fetchData/route.ts index 88febb8c4..1229e0edd 100644 --- a/src/app/api/fetchData/route.ts +++ b/src/app/api/fetchData/route.ts @@ -8,7 +8,7 @@ const clientWithToken = client.withConfig({ token }); interface FetchRequestBody { query: string; - params?: Record; + params?: Record; } export async function POST(req: Request) { diff --git a/src/components/richText/RichText.tsx b/src/components/richText/RichText.tsx index 918d8817a..a4d991e9e 100644 --- a/src/components/richText/RichText.tsx +++ b/src/components/richText/RichText.tsx @@ -1,16 +1,19 @@ "use client"; -import { PortableText } from "@portabletext/react"; +import { PortableText, PortableTextReactComponents } from "@portabletext/react"; +import { PortableTextObject } from "@sanity/types"; +import { ReactNode } from "react"; import Text from "src/components/text/Text"; import textStyles from "src/components/text/text.module.css"; import { useConvertSanityImageToNextImage } from "src/utils/hooks/useConvertImage"; +import { getReactNodeTextContent } from "src/utils/reactNode"; import styles from "./richText.module.css"; type Children = { _type: string; - marks: any[]; + marks: string[]; text: string; _key: string; }; @@ -27,11 +30,11 @@ export type PortableTextBlock = { _type: string; }; alt?: string; - markDefs?: any[]; + markDefs?: PortableTextObject[]; }; -const formatId = (children: any): string => { - const text = children.join(" "); +const formatId = (children: ReactNode): string => { + const text = getReactNodeTextContent(children); return text .toLowerCase() @@ -44,36 +47,32 @@ const SanityImage = ({ value }: { value: PortableTextBlock }) => { return
{ImageElement}
; }; -const myPortableTextComponents = { +const myPortableTextComponents: Partial = { block: { - h2: ({ children }: any) => ( + h2: ({ children }) => ( {children} ), - h3: ({ children }: any) => ( + h3: ({ children }) => ( {children} ), - normal: ({ children }: any) => {children}, - blockquote: ({ children }: any) => ( + normal: ({ children }) => {children}, + blockquote: ({ children }) => (
{children}
), }, list: { - bullet: ({ children }: any) =>
    {children}
, - number: ({ children }: any) =>
    {children}
, + bullet: ({ children }) =>
    {children}
, + number: ({ children }) =>
    {children}
, }, listItem: { - bullet: ({ children }: any) => ( -
  • {children}
  • - ), - number: ({ children }: any) => ( -
  • {children}
  • - ), + bullet: ({ children }) =>
  • {children}
  • , + number: ({ children }) =>
  • {children}
  • , }, types: { image: SanityImage, diff --git a/src/components/sections/callout/Callout.tsx b/src/components/sections/callout/Callout.tsx index 8c2f84f14..850660211 100644 --- a/src/components/sections/callout/Callout.tsx +++ b/src/components/sections/callout/Callout.tsx @@ -1,4 +1,4 @@ -import { PortableText } from "@portabletext/react"; +import { PortableText, PortableTextReactComponents } from "@portabletext/react"; import CustomLink from "src/components/link/CustomLink"; import Text from "src/components/text/Text"; @@ -10,8 +10,8 @@ interface CalloutProps { callout: CalloutSection; } -const myPortableTextComponents = { - block: ({ children }: any) => {children}, +const myPortableTextComponents: Partial = { + block: ({ children }) => {children}, }; const Callout = ({ callout }: CalloutProps) => { diff --git a/src/components/sections/grid/Grid.tsx b/src/components/sections/grid/Grid.tsx index c35b28586..c0815f396 100644 --- a/src/components/sections/grid/Grid.tsx +++ b/src/components/sections/grid/Grid.tsx @@ -1,5 +1,5 @@ "use client"; -import { PortableText } from "@portabletext/react"; +import { PortableText, PortableTextReactComponents } from "@portabletext/react"; import { PortableTextBlock } from "src/components/richText/RichText"; import Text from "src/components/text/Text"; @@ -54,6 +54,6 @@ const Element = ({ ); }; -const myPortableTextComponents = { - block: ({ children }: any) => {children}, +const myPortableTextComponents: Partial = { + block: ({ children }) => {children}, }; diff --git a/src/components/sections/logoSalad/LogoSalad.tsx b/src/components/sections/logoSalad/LogoSalad.tsx index 94da98028..639a84884 100644 --- a/src/components/sections/logoSalad/LogoSalad.tsx +++ b/src/components/sections/logoSalad/LogoSalad.tsx @@ -1,4 +1,4 @@ -import { PortableText } from "@portabletext/react"; +import { PortableText, PortableTextReactComponents } from "@portabletext/react"; import Text from "src/components/text/Text"; import { LogoSaladSection } from "studio/lib/interfaces/pages"; @@ -10,8 +10,8 @@ interface LogoSaladProps { logoSalad: LogoSaladSection; } -const myPortableTextComponents = { - block: ({ children }: any) => {children}, +const myPortableTextComponents: Partial = { + block: ({ children }) => {children}, }; export const LogoSalad = ({ logoSalad }: LogoSaladProps) => { diff --git a/src/post/lead/Lead.tsx b/src/post/lead/Lead.tsx index 2e081b64a..131e1d845 100644 --- a/src/post/lead/Lead.tsx +++ b/src/post/lead/Lead.tsx @@ -1,5 +1,5 @@ "use client"; -import { PortableText } from "@portabletext/react"; +import { PortableText, PortableTextReactComponents } from "@portabletext/react"; import { PortableTextBlock } from "src/components/richText/RichText"; import Text from "src/components/text/Text"; @@ -8,8 +8,8 @@ import { IImage } from "studio/lib/interfaces/media"; import styles from "./lead.module.css"; -const myPortableTextComponents = { - block: ({ children }: any) => {children}, +const myPortableTextComponents: Partial = { + block: ({ children }) => {children}, }; const Lead = ({ diff --git a/src/utils/reactNode.ts b/src/utils/reactNode.ts new file mode 100644 index 000000000..f2eef11b7 --- /dev/null +++ b/src/utils/reactNode.ts @@ -0,0 +1,20 @@ +import { ReactNode, isValidElement } from "react"; + +export function getReactNodeTextContent(node: ReactNode): string { + if (typeof node === "string" || typeof node === "number") { + return node.toString(); + } + + if (Array.isArray(node)) { + return node.map(getReactNodeTextContent).join(""); + } + + if (isValidElement(node)) { + const children = node.props.children; + if (children) { + return getReactNodeTextContent(children); + } + } + + return ""; +} diff --git a/src/utils/seo.ts b/src/utils/seo.ts index d1ad7457e..bdd5140ad 100644 --- a/src/utils/seo.ts +++ b/src/utils/seo.ts @@ -1,4 +1,5 @@ import { toPlainText } from "@portabletext/toolkit"; +import type { QueryParams } from "@sanity/client"; import { Metadata } from "next"; import { PortableTextBlock } from "src/components/richText/RichText"; @@ -32,7 +33,7 @@ export const OPEN_GRAPH_IMAGE_DIMENSIONS = { export async function fetchSeoData( query: string, - variables?: any, + variables?: QueryParams | undefined, ): Promise { try { const { data } = await loadQuery(query, variables); @@ -45,7 +46,7 @@ export async function fetchSeoData( export async function fetchPostSeoData( query: string, - variables?: any, + variables?: QueryParams | undefined, ): Promise { try { const { data } = await loadQuery(query, variables); diff --git a/studio/components/AnchorSelect.tsx b/studio/components/AnchorSelect.tsx index 1d68f3d97..3d878f266 100644 --- a/studio/components/AnchorSelect.tsx +++ b/studio/components/AnchorSelect.tsx @@ -1,17 +1,9 @@ import { Button, Select, Stack } from "@sanity/ui"; import React, { useEffect, useState } from "react"; -import { PatchEvent, set, unset, useFormValue } from "sanity"; +import { PatchEvent, StringInputProps, set, unset, useFormValue } from "sanity"; import { fetchWithToken } from "studio/lib/fetchWithToken"; -interface AnchorSelectProps { - value?: string; - type: any; - onChange: (event: PatchEvent) => void; - path: any[]; - schemaType: any; -} - interface AnchorItem { basicTitle?: string; value: string; @@ -30,7 +22,7 @@ function fromCamelCase(value?: string) { }); // Capitalize the first letter } -const AnchorSelect = ({ value, onChange, path }: AnchorSelectProps) => { +const AnchorSelect = ({ value, onChange, path }: StringInputProps) => { const [listItems, setListItems] = useState([]); // Extract the internal link reference from the form value diff --git a/studio/components/SoMeInputs.tsx b/studio/components/SoMeInputs.tsx index 322bbbb6c..3707786fe 100644 --- a/studio/components/SoMeInputs.tsx +++ b/studio/components/SoMeInputs.tsx @@ -2,6 +2,8 @@ import { Box, Label, Select, Stack, TextInput } from "@sanity/ui"; import React from "react"; import { ObjectInputProps, set } from "sanity"; +import { SocialMediaLink } from "studio/lib/interfaces/socialMedia"; + export const SoMePlatforms: { [key: string]: string } = { facebook: "Facebook", x: "X", @@ -23,10 +25,28 @@ const detectPlatformFromUrl = (url: string): string | null => { return null; }; -const SoMeInputs: React.FC>> = ({ +function isSocialMediaLinkType(value: unknown): value is SocialMediaLink { + return ( + typeof value === "object" && + value !== null && + "_type" in value && + typeof value._type === "string" && + "url" in value && + typeof value.url === "string" && + "platform" in value && + typeof value.platform === "string" + ); +} + +const SoMeInputs: React.FC>> = ({ value = {}, onChange, }) => { + if (!isSocialMediaLinkType(value)) { + console.error("Unexpected value type for SoMeInputs"); + return; + } + const handleUrlChange = (event: React.ChangeEvent) => { if (!onChange) return; const newUrl = event.target.value; @@ -58,7 +78,7 @@ const SoMeInputs: React.FC>> = ({ @@ -72,7 +92,7 @@ const SoMeInputs: React.FC>> = ({