Skip to content

Commit

Permalink
feat: useSeo
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasio committed Aug 25, 2023
1 parent 4275962 commit 182be6f
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/popular-turkeys-sleep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@headstartwp/core": minor
---

Introducing the `useSeo` hook.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ module.exports = {
assertFunctionNames: ['expect', 'expectTypeOf'],
},
],
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'error',
},
settings: {
'import/resolver': 'typescript',
Expand Down
24 changes: 24 additions & 0 deletions docs/documentation/02 - Data Fetching/useSeo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
slug: /data-fetching/useSeo
sidebar_position: 7
---

# The useSeo hook

:::info
This hook was introduced in `@headstartwp/next@1.1.0`
:::info

The `useSeo` hook returns the SEO data for the current page.

## Basic Usage

```javascript
// by default it returns the json object
const yoast_json = useSeo();
const yoast_json = useSeo('json');
// but you can also get the plain html markup for the metadata
const yoast_head = useSeo('html');
```

If there's no seo information for the current route, this hook will return `null`.
105 changes: 105 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/core/src/react/hooks/useFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ export interface useFetchOptions {
shouldFetch?: () => boolean;
}

// re-export useSWR
export { useSWR, useSWRConfig };

/**
* The use Fetch Hook is the foundation for most hooks in the headless framework. It is a wrapper around
* `useSWR` and provides a consistent API for fetching data from the API. It requires a fetch strategy which implements
Expand Down
15 changes: 14 additions & 1 deletion packages/next/src/components/HeadlessApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { SettingsContextProps } from '@headstartwp/core/react';
import { useRouter } from 'next/router';
import { getSiteByHost } from '@headstartwp/core';
import { Yoast } from './Yoast';
import { seoKey } from '../data/hooks/useSeo';

/**
* The props supported by {@link HeadlessApp}.
Expand Down Expand Up @@ -47,6 +48,13 @@ export type HeadlessAppProps = {
*/
useYoastHtml?: boolean;

/**
* If true, will automatically load yoast seo metadata into the head
*
* @default true
*/
handleYoast?: boolean;

children?: ReactNode;
};

Expand Down Expand Up @@ -91,6 +99,7 @@ export function HeadlessApp({
pageProps,
swrConfig = {},
useYoastHtml = false,
handleYoast = true,
}: HeadlessAppProps) {
const { fallback = {}, seo = {}, themeJSON = { settings: {}, styles: {} } } = pageProps;
const router = useRouter();
Expand All @@ -102,6 +111,10 @@ export function HeadlessApp({
swrConfig.revalidateOnMount = false;
}

if (typeof seo?.yoast_head_json !== 'undefined' || typeof seo?.yoast_head !== 'undefined') {
fallback[seoKey] = seo;
}

const currentSite = useMemo(() => {
if (router.query?.site && !Array.isArray(router.query.site)) {
return getSiteByHost(router.query.site, router.locale);
Expand All @@ -118,7 +131,7 @@ export function HeadlessApp({
swrConfig={swrConfig}
data={fallback as DataFetchingProviderProps['swrConfig']['fallback']}
>
<Yoast seo={seo} useHtml={useYoastHtml} />
{handleYoast ? <Yoast seo={seo} useHtml={useYoastHtml} /> : null}
<ThemeSettingsProvider data={themeJSON}>{children}</ThemeSettingsProvider>
</DataFetchingProvider>
</SettingsProvider>
Expand Down
70 changes: 70 additions & 0 deletions packages/next/src/data/hooks/__tests__/useSeo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { DataFetchingProvider } from '@headstartwp/core/react';
import { renderHook } from '@testing-library/react';
import * as React from 'react';
import { useSeo } from '../useSeo';

describe('useSeo', () => {
it('returns seo data', () => {
const wrapper = ({ children }) => {
return (
<DataFetchingProvider
swrConfig={{}}
data={{
'@seo': {
yoast_head_json: { title: 'test' },
yoast_head: '<meta name="title" value="test" />',
},
}}
>
{children}
</DataFetchingProvider>
);
};

const { result } = renderHook(() => useSeo(), {
wrapper,
});

expect(result.current).toMatchObject({ title: 'test' });

const { result: result2 } = renderHook(() => useSeo('html'), {
wrapper,
});

expect(result2.current).toBe('<meta name="title" value="test" />');

const { result: result3 } = renderHook(() => useSeo('json'), {
wrapper,
});

expect(result3.current).toMatchObject({ title: 'test' });
});

it('returns null if seo data is not set', () => {
const wrapper = ({ children }) => {
return (
<DataFetchingProvider swrConfig={{}} data={{}}>
{children}
</DataFetchingProvider>
);
};

const { result } = renderHook(() => useSeo(), {
wrapper,
});

expect(result.current).toBeNull();

const { result: result2 } = renderHook(() => useSeo('html'), {
wrapper,
});

expect(result2.current).toBeNull();

const { result: result3 } = renderHook(() => useSeo('json'), {
wrapper,
});

expect(result3.current).toBeNull();
});
});
1 change: 1 addition & 0 deletions packages/next/src/data/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './useTerms';
export * from './useAuthorArchive';
export * from './usePrepareFetch';
export * from './usePostOrPosts';
export * from './useSeo';
35 changes: 35 additions & 0 deletions packages/next/src/data/hooks/useSeo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useSWRConfig } from '@headstartwp/core/react';

export const seoKey = '@seo';

/**
* The useSeo hook. Returns the current SEO object
*
* ## Usage
*
* ```tsx
* const seo = useSeo();
* ```
*
* @param format how to return the seo object. Defaults to 'json'
*
* @category Data Fetching Hooks
*/
function useSeo(format: 'json'): Record<string, any> | null;
function useSeo(format: 'html'): string | null;
function useSeo(): Record<string, any> | null;
function useSeo(format?: 'json' | 'html') {
const { fallback } = useSWRConfig();

if (typeof fallback[seoKey] === 'undefined') {
return null;
}

if (format === 'json' || typeof format === 'undefined') {
return fallback[seoKey]?.yoast_head_json as Record<string, any>;
}

return fallback[seoKey]?.yoast_head as string;
}

export { useSeo };

0 comments on commit 182be6f

Please sign in to comment.