Tweaked type definitions in signup form

This commit is contained in:
Jono Mingard 2023-05-24 15:53:04 +12:00
parent af907b70cb
commit 03fb1b188c
6 changed files with 32 additions and 33 deletions

View File

@ -1,15 +1,15 @@
import React, {ComponentProps} from 'react'; import React, {ComponentProps} from 'react';
import pages, {Page, PageName} from './pages'; import pages, {Page, PageName} from './pages';
import {AppContext, SignupFormOptions} from './AppContext'; import {AppContextProvider, SignupFormOptions} from './AppContext';
import {ContentBox} from './components/ContentBox'; import {ContentBox} from './components/ContentBox';
import {Frame} from './components/Frame'; import {Frame} from './components/Frame';
import {setupGhostApi} from './utils/api'; import {setupGhostApi} from './utils/api';
type Props = { type AppProps = {
options: SignupFormOptions; options: SignupFormOptions;
}; };
const App: React.FC<Props> = ({options}) => { const App: React.FC<AppProps> = ({options}) => {
const [page, setPage] = React.useState<Page>({ const [page, setPage] = React.useState<Page>({
name: 'FormPage', name: 'FormPage',
data: {} data: {}
@ -38,13 +38,13 @@ const App: React.FC<Props> = ({options}) => {
return ( return (
<div> <div>
<AppContext.Provider value={context}> <AppContextProvider value={context}>
<Frame> <Frame>
<ContentBox> <ContentBox>
<PageComponent {...data} /> <PageComponent {...data} />
</ContentBox> </ContentBox>
</Frame> </Frame>
</AppContext.Provider> </AppContextProvider>
</div> </div>
); );
}; };

View File

@ -1,5 +1,5 @@
// Ref: https://reactjs.org/docs/context.html // Ref: https://reactjs.org/docs/context.html
import React, {ComponentProps} from 'react'; import React, {ComponentProps, useContext} from 'react';
import pages, {Page, PageName} from './pages'; import pages, {Page, PageName} from './pages';
import {GhostApi} from './utils/api'; import {GhostApi} from './utils/api';
@ -19,4 +19,8 @@ export type AppContextType = {
api: GhostApi, api: GhostApi,
} }
export const AppContext = React.createContext<AppContextType>({} as any); const AppContext = React.createContext<AppContextType>({} as any);
export const AppContextProvider = AppContext.Provider;
export const useAppContext = () => useContext(AppContext);

View File

@ -1,12 +1,12 @@
import React from 'react'; import React from 'react';
import {AppContext} from '../AppContext'; import {useAppContext} from '../AppContext';
type Props = { type ContentBoxProps = {
children: React.ReactNode children: React.ReactNode
}; };
export const ContentBox: React.FC<Props> = ({children}) => { export const ContentBox: React.FC<ContentBoxProps> = ({children}) => {
const {color} = React.useContext(AppContext).options; const {color} = useAppContext().options;
const style = { const style = {
'--gh-accent-color': color '--gh-accent-color': color

View File

@ -1,12 +1,10 @@
import React, {FormEventHandler} from 'react'; import React, {FormEventHandler} from 'react';
import {AppContext} from '../../AppContext';
import {isMinimal} from '../../utils/helpers'; import {isMinimal} from '../../utils/helpers';
import {isValidEmail} from '../../utils/validator'; import {isValidEmail} from '../../utils/validator';
import {useAppContext} from '../../AppContext';
type Props = {}; export const FormPage: React.FC = () => {
const {options} = useAppContext();
export const FormPage: React.FC<Props> = () => {
const {options} = React.useContext(AppContext);
if (isMinimal(options)) { if (isMinimal(options)) {
return ( return (
@ -27,11 +25,11 @@ export const FormPage: React.FC<Props> = () => {
</div>; </div>;
}; };
const Form: React.FC<Props> = () => { const Form: React.FC = () => {
const [email, setEmail] = React.useState(''); const [email, setEmail] = React.useState('');
const [error, setError] = React.useState(''); const [error, setError] = React.useState('');
const [loading, setLoading] = React.useState(false); const [loading, setLoading] = React.useState(false);
const {api, setPage, options} = React.useContext(AppContext); const {api, setPage, options} = useAppContext();
const labels = options.labels; const labels = options.labels;
const submit: FormEventHandler<HTMLFormElement> = async (e) => { const submit: FormEventHandler<HTMLFormElement> = async (e) => {

View File

@ -1,13 +1,13 @@
import React from 'react'; import React from 'react';
import {AppContext} from '../../AppContext';
import {isMinimal} from '../../utils/helpers'; import {isMinimal} from '../../utils/helpers';
import {useAppContext} from '../../AppContext';
type Props = { type SuccessPageProps = {
email: string; email: string;
}; };
export const SuccessPage: React.FC<Props> = ({email}) => { export const SuccessPage: React.FC<SuccessPageProps> = ({email}) => {
const {options} = React.useContext(AppContext); const {options} = useAppContext();
if (isMinimal(options)) { if (isMinimal(options)) {
return <div> return <div>

View File

@ -2,23 +2,20 @@ import React from 'react';
import {FormPage} from './components/pages/FormPage'; import {FormPage} from './components/pages/FormPage';
import {SuccessPage} from './components/pages/SuccessPage'; import {SuccessPage} from './components/pages/SuccessPage';
// When adding a new page, also add it at the bottom to the Page type (*)
const Pages = { const Pages = {
FormPage, FormPage,
SuccessPage SuccessPage
}; };
// (*) Note we have repeated names here, and don't use PageName
// to make type checking work for the Page type, so we have type checks in place
// that we pass the right data to the right page (otherwise it will only check if
// the name is correct, and the data is correct for any page, not the specific page)
export type Page = PageType<'FormPage'> | PageType<'SuccessPage'>;
export type PageName = keyof typeof Pages; export type PageName = keyof typeof Pages;
export type PageType<Name extends PageName> = {
name: Name; type PageTypes = {
// get props of component [name in PageName]: {
data: React.ComponentProps<typeof Pages[Name]>; name: name,
data: React.ComponentProps<typeof Pages[name]>
}
} }
export type Page = PageTypes[keyof PageTypes]
export default Pages; export default Pages;