Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,22 @@ Given `npm install` / `npm ci` floods the terminal with `npm warn deprecated …
3. Prefer fixing/removing those roots over adding an `override` per leaf package;
4. Keep using Node 18 + `npm install --legacy-peer-deps` for this stack.

## Jest PropTypes: SEO required `description` undefined

Given the browser console shows:

```
Warning: Failed prop type: The prop `description` is marked as required in `SEO`, but its value is `undefined`.
at SEO (…/metax/…)
at SEO (src/components/SEO/…)
at IndexTemplate (src/templates/index.js)
```

1. Ensure `src/components/SEO` defaults `description` (and `title`) from `useSiteMetadata()` when omitted;
2. Prefer page-specific `description` on important routes (e.g. home `IndexTemplate`);
3. Upstream: [muy/muy#154](https://github.com/muy/muy/issues/154) / PR for `metax` `mergeProps` fallback;
4. See #455.

## Gatsby HMR eslint-loader: unused vars / anonymous page exports

Given the browser console (or terminal) shows eslint-loader module warnings such as:
Expand Down
144 changes: 75 additions & 69 deletions src/components/SEO/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,80 @@ import React from "react"
import { SEO as MetaxSEO } from "metax"
import useSiteMetadata from "../../hooks/useSiteMetadata"

const SEO = (props) => (
<MetaxSEO
bodyAttributes={[{ onTouchStart: "" }]}
link={[
{
href: "/iphone5_splash.png",
media:
"(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/iphone6_splash.png",
media:
"(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/iphoneplus_splash.png",
media:
"(device-width: 621px) and (device-height: 1104px) and (-webkit-device-pixel-ratio: 3)",
rel: "apple-touch-startup-image",
},
{
href: "/iphonex_splash.png",
media:
"(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3)",
rel: "apple-touch-startup-image",
},
{
href: "/iphonexr_splash.png",
media:
"(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/iphonexsmax_splash.png",
media:
"(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3)",
rel: "apple-touch-startup-image",
},
{
href: "/ipad_splash.png",
media:
"(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/ipadpro1_splash.png",
media:
"(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/ipadpro3_splash.png",
media:
"(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/ipadpro2_splash.png",
media:
"(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
]}
siteMetadata={useSiteMetadata()}
{...props}
/>
)
const SEO = ({ description, title, ...props }) => {
const siteMetadata = useSiteMetadata()

return (
<MetaxSEO
bodyAttributes={[{ onTouchStart: "" }]}
description={description ?? siteMetadata.description}
link={[
{
href: "/iphone5_splash.png",
media:
"(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/iphone6_splash.png",
media:
"(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/iphoneplus_splash.png",
media:
"(device-width: 621px) and (device-height: 1104px) and (-webkit-device-pixel-ratio: 3)",
rel: "apple-touch-startup-image",
},
{
href: "/iphonex_splash.png",
media:
"(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3)",
rel: "apple-touch-startup-image",
},
{
href: "/iphonexr_splash.png",
media:
"(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/iphonexsmax_splash.png",
media:
"(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3)",
rel: "apple-touch-startup-image",
},
{
href: "/ipad_splash.png",
media:
"(device-width: 768px) and (device-height: 1024px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/ipadpro1_splash.png",
media:
"(device-width: 834px) and (device-height: 1112px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/ipadpro3_splash.png",
media:
"(device-width: 834px) and (device-height: 1194px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
{
href: "/ipadpro2_splash.png",
media:
"(device-width: 1024px) and (device-height: 1366px) and (-webkit-device-pixel-ratio: 2)",
rel: "apple-touch-startup-image",
},
]}
siteMetadata={siteMetadata}
title={title ?? siteMetadata.title}
{...props}
/>
)
}

export default SEO
65 changes: 65 additions & 0 deletions src/components/SEO/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from "react"
import { render } from "@testing-library/react"
import SEO from "."

jest.mock("../../hooks/useSiteMetadata", () => () => ({
description: "Site default description",
title: "Multei!",
titleTemplate: "%s | Multei!",
}))

const mockMetaxSEO = jest.fn(() => null)

jest.mock("metax", () => ({
SEO: (props) => {
mockMetaxSEO(props)
return null
},
}))

describe("<SEO />", () => {
beforeEach(() => {
mockMetaxSEO.mockClear()
})

it("defaults description and title from site metadata", () => {
const consoleError = jest
.spyOn(console, "error")
.mockImplementation(() => {})

render(<SEO />)

expect(mockMetaxSEO).toHaveBeenCalledWith(
expect.objectContaining({
description: "Site default description",
title: "Multei!",
})
)

const descriptionWarnings = consoleError.mock.calls.filter((args) =>
args.some(
(arg) =>
typeof arg === "string" &&
arg.includes("prop `description` is marked as required")
)
)
expect(descriptionWarnings).toHaveLength(0)
consoleError.mockRestore()
})

it("prefers explicit description and title props", () => {
render(
<SEO
description="Page-specific description"
title="Denunciar estacionamento irregular"
/>
)

expect(mockMetaxSEO).toHaveBeenCalledWith(
expect.objectContaining({
description: "Page-specific description",
title: "Denunciar estacionamento irregular",
})
)
})
})
1 change: 1 addition & 0 deletions src/templates/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const IndexTemplate = () => {
return (
<Article>
<SEO
description="Denuncie estacionamento irregular em vagas preferenciais sem credencial"
title="Denunciar estacionamento irregular"
titleTemplate={"Multei! %s"}
/>
Expand Down
Loading