diff --git a/components/author/context.js b/components/author/context.js deleted file mode 100644 index a711fea6..00000000 --- a/components/author/context.js +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from '@wordpress/element'; - -export const AuthorContext = createContext(); diff --git a/components/author/context.ts b/components/author/context.ts new file mode 100644 index 00000000..e11f0e24 --- /dev/null +++ b/components/author/context.ts @@ -0,0 +1,35 @@ +import { createContext, useContext } from '@wordpress/element'; + +export type Author = { + avatar_urls: Record; + description: string; + email: string; + first_name: string; + id: number; + last_name: string; + link: string; + name: string; + nickname: string; + registered_date: string; + slug: string; + url: string; +}; + +export const AuthorContext = createContext({ + avatar_urls: {}, + description: '', + email: '', + first_name: '', + id: 0, + last_name: '', + link: '', + name: '', + nickname: '', + registered_date: '', + slug: '', + url: '', +}); + +export const useAuthor = () => { + return useContext(AuthorContext); +} diff --git a/components/author/index.js b/components/author/index.js deleted file mode 100644 index 11f52641..00000000 --- a/components/author/index.js +++ /dev/null @@ -1,144 +0,0 @@ -import { useContext } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; -import { store as blockEditorStore } from '@wordpress/block-editor'; -import PropTypes from 'prop-types'; -import { AuthorContext } from './context'; - -/** - * @typedef {object} Author - * @property {object} author - * @property {object} author.avatar_urls - * @property {string} author.description - * @property {string} author.email - * @property {string} author.first_name - * @property {number} author.id - * @property {string} author.last_name - * @property {string} author.link - * @property {string} author.name - * @property {string} author.nickname - * @property {string} author.registered_date - * @property {string} author.slug - * @property {string} author.url - */ - -export const Name = (props) => { - const { tagName: TagName = 'span', ...rest } = props; - - /** - * @type {Author} - */ - const { name, link } = useContext(AuthorContext); - - const wrapperProps = { ...rest }; - - if (TagName === 'a' && link) { - wrapperProps.href = link; - } - - return {name}; -}; - -Name.propTypes = { - tagName: PropTypes.string, -}; - -Name.defaultProps = { - tagName: 'span', -}; - -export const FirstName = (props) => { - const { tagName: TagName = 'span', ...rest } = props; - - /** - * @type {Author} - */ - const { first_name: firstName } = useContext(AuthorContext); - - return {firstName}; -}; - -FirstName.propTypes = { - tagName: PropTypes.string, -}; - -FirstName.defaultProps = { - tagName: 'span', -}; - -export const LastName = (props) => { - const { tagName: TagName = 'span', ...rest } = props; - - /** - * @type {Author} - */ - const { last_name: lastName } = useContext(AuthorContext); - - return {lastName}; -}; - -LastName.propTypes = { - tagName: PropTypes.string, -}; - -LastName.defaultProps = { - tagName: 'span', -}; - -function useDefaultAvatar() { - const { avatarURL: defaultAvatarUrl } = useSelect((select) => { - const { getSettings } = select(blockEditorStore); - const { __experimentalDiscussionSettings } = getSettings(); - return __experimentalDiscussionSettings; - }); - return defaultAvatarUrl; -} - -export const Avatar = (props) => { - const { ...rest } = props; - - /** - * @type {Author} - */ - const authorDetails = useContext(AuthorContext); - - const avatarUrls = authorDetails?.avatar_urls ? Object.values(authorDetails.avatar_urls) : null; - const defaultAvatar = useDefaultAvatar(); - - const avatarSourceUrl = avatarUrls ? avatarUrls[avatarUrls.length - 1] : defaultAvatar; - - return ; -}; - -export const Bio = (props) => { - const { tagName: TagName = 'p', ...rest } = props; - - /** - * @type {Author} - */ - const { description } = useContext(AuthorContext); - - return {description}; -}; - -Bio.propTypes = { - tagName: PropTypes.string, -}; - -Bio.defaultProps = { - tagName: 'p', -}; - -export const Email = (props) => { - const { ...rest } = props; - - /** - * @type {Author} - */ - const { email } = useContext(AuthorContext); - - return ( - - {email} - - ); -}; diff --git a/components/author/index.tsx b/components/author/index.tsx new file mode 100644 index 00000000..5e324fb9 --- /dev/null +++ b/components/author/index.tsx @@ -0,0 +1,97 @@ +import { useSelect } from '@wordpress/data'; +import { store as blockEditorStore } from '@wordpress/block-editor'; +import { useAuthor } from './context'; + +interface NameProps { + tagName?: keyof JSX.IntrinsicElements; + [key: string]: any; +} + +export const Name: React.FC = (props) => { + const { tagName: TagName = 'span', ...rest } = props; + const { name, link } = useAuthor(); + + const wrapperProps = { ...rest }; + + if (TagName === 'a' && link) { + wrapperProps.href = link; + } + + return {name}; +}; + +interface FirstNameProps { + tagName?: keyof JSX.IntrinsicElements; + [key: string]: any; +} + +export const FirstName: React.FC = (props) => { + const { tagName: TagName = 'span', ...rest } = props; + const { first_name: firstName } = useAuthor(); + + return {firstName}; +}; + +interface LastNameProps { + tagName?: keyof JSX.IntrinsicElements; + [key: string]: any; +} + +export const LastName: React.FC = (props) => { + const { tagName: TagName = 'span', ...rest } = props; + const { last_name: lastName } = useAuthor(); + + return {lastName}; +}; + +function useDefaultAvatar() { + const { avatarURL: defaultAvatarUrl } = useSelect((select) => { + const { getSettings } = select(blockEditorStore); + const { __experimentalDiscussionSettings } = getSettings(); + return __experimentalDiscussionSettings; + }, []); + return defaultAvatarUrl; +} + +interface AvatarProps { + [key: string]: any; +} + +export const Avatar: React.FC = (props) => { + const { ...rest } = props; + const authorDetails = useAuthor(); + + const avatarUrls = authorDetails?.avatar_urls ? Object.values(authorDetails.avatar_urls) : null; + const defaultAvatar = useDefaultAvatar(); + + const avatarSourceUrl = avatarUrls ? avatarUrls[avatarUrls.length - 1] : defaultAvatar; + + return ; +}; + +interface BioProps { + tagName?: keyof JSX.IntrinsicElements; + [key: string]: any; +} + +export const Bio: React.FC = (props) => { + const { tagName: TagName = 'p', ...rest } = props; + const { description } = useAuthor(); + + return {description}; +}; + +interface EmailProps { + [key: string]: any; +} + +export const Email: React.FC = (props) => { + const { ...rest } = props; + const { email } = useAuthor(); + + return ( + + {email} + + ); +}; diff --git a/components/post-author/index.js b/components/post-author/index.tsx similarity index 77% rename from components/post-author/index.js rename to components/post-author/index.tsx index d8a60bb8..b25e8c17 100644 --- a/components/post-author/index.js +++ b/components/post-author/index.tsx @@ -2,13 +2,25 @@ import { Children } from '@wordpress/element'; import { store as coreStore } from '@wordpress/core-data'; import { Spinner } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; -import PropTypes from 'prop-types'; import { usePost } from '../../hooks'; import { Name, FirstName, LastName, Avatar, Bio, Email } from '../author'; import { AuthorContext } from '../author/context'; - -export const PostAuthor = (props) => { +import type { Author } from '../author/context'; + +interface PostAuthorProps { + children?: React.ReactNode | ((author: Author) => React.ReactNode); + [key: string]: any; +} + +export const PostAuthor: React.FC & { + Name: typeof Name; + FirstName: typeof FirstName; + LastName: typeof LastName; + Avatar: typeof Avatar; + Bio: typeof Bio; + Email: typeof Email; +} = (props) => { const { children, ...rest } = props; const { postId, postType } = usePost(); @@ -16,7 +28,7 @@ export const PostAuthor = (props) => { (select) => { const { getEditedEntityRecord, getUser, hasFinishedResolution } = select(coreStore); - const postQuery = ['postType', postType, postId]; + const postQuery = ['postType', postType, postId as number] as const; const post = getEditedEntityRecord(...postQuery); const hasResolvedPost = hasFinishedResolution('getEditedEntityRecord', postQuery); @@ -54,18 +66,6 @@ export const PostAuthor = (props) => { return ; }; -PostAuthor.propTypes = { - children: PropTypes.oneOfType([ - PropTypes.func, - PropTypes.node, - PropTypes.arrayOf(PropTypes.node), - ]), -}; - -PostAuthor.defaultProps = { - children: null, -}; - PostAuthor.Name = Name; PostAuthor.FirstName = FirstName; PostAuthor.LastName = LastName;