Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev: source setting screens from global #147

Merged
merged 1 commit into from
Oct 26, 2024
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
4 changes: 2 additions & 2 deletions packages/admin/components/fields/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AllowedSettingKeys, SettingSchema } from '@/admin/types';
import type { FieldSchema } from '@/admin/types';
import { Field } from './field';

export const Fields = ( {
Expand All @@ -9,7 +9,7 @@ export const Fields = ( {
}: {
excludedProperties?: string[];
values: Record< string, unknown > | undefined;
fields: SettingSchema[ AllowedSettingKeys ];
fields: Record< string, FieldSchema >;
setValue: ( values: Record< string, unknown > ) => void;
} ) => {
if ( ! values ) {
Expand Down
70 changes: 42 additions & 28 deletions packages/admin/components/layout/header/menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Button, Icon, NavigableMenu } from '@wordpress/components';
import { link as LinkSVG } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { useCurrentScreen } from '@/admin/components/screen/context';
import type { AllowedScreens } from '@/admin/components/screen/screen';
import { getScreenForSetting } from '@/admin/components/screen/utils';

import styles from './styles.module.scss';

Expand All @@ -13,43 +13,57 @@ const LinkIcon = () => {
return <Icon icon={ LinkSVG } className={ styles.linkIcon } size={ 16 } />;
};

const SCREEN_LABELS: Record< AllowedScreens, string > = {
providers: __( 'Login Providers', 'wp-graphql-headless-login' ),
'access-control': __( 'Access Control', 'wp-graphql-headless-login' ),
'plugin-settings': __( 'Misc', 'wp-graphql-headless-login' ),
/**
* Builds and returns the menu object from the wpGraphQLLogin.settings global.
*/
export const getMenuObject = (): Record< string, string > => {
const settings = wpGraphQLLogin.settings;

const menu: Record< string, string > = {
providers: '', // We want this as the first key.
};

for ( const key in settings ) {
// @todo get providers from global after the refactor.
const menuTitle =
settings[ key ].label ||
__( 'Providers', 'wp-graphql-headless-login' );
const screen = getScreenForSetting( key );

menu[ screen ] = menuTitle;
}

return menu;
};

export const Menu = () => {
const { currentScreen, setCurrentScreen } = useCurrentScreen();

// Build the menu object of screens and labels from the wpGraphQLLogin?.settings.
const menuItems = getMenuObject();

return (
<NavigableMenu orientation="horizontal">
<ul role="menubar" className={ styles.menu }>
{
// Loop through the screen titles and create a button for each one.
Object.entries( SCREEN_LABELS ).map(
( [ screen, title ] ) => (
<li key={ screen }>
<Button
key={ screen }
className={
currentScreen === screen
? styles.active
: ''
}
variant="tertiary"
onClick={ () =>
setCurrentScreen(
screen as AllowedScreens
)
}
role="menuitem"
>
{ title }
</Button>
</li>
)
)
Object.entries( menuItems ).map( ( [ screen, title ] ) => (
<li key={ screen }>
<Button
key={ screen }
className={
currentScreen === screen
? styles.active
: ''
}
variant="tertiary"
onClick={ () => setCurrentScreen( screen ) }
role="menuitem"
>
{ title }
</Button>
</li>
) )
}
<li>
<Button
Expand Down
13 changes: 7 additions & 6 deletions packages/admin/components/screen/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,29 @@ import {
useContext,
startTransition,
} from 'react';
import { SCREEN_MAP, type AllowedScreens } from './screen';
import { isAllowedScreen } from './utils';

const ScreenContext = createContext< {
currentScreen: AllowedScreens;
setCurrentScreen: ( screen: AllowedScreens ) => void;
currentScreen: string;
setCurrentScreen: ( screen: string ) => void;
} >( {
currentScreen: 'providers',
setCurrentScreen: () => {}, // eslint-disable-line @typescript-eslint/no-empty-function
} );

export const ScreenProvider = ( { children }: PropsWithChildren ) => {
const [ currentScreen, setCurrentScreen ] =
useState< AllowedScreens >( 'providers' );
useState< string >( 'providers' );

// The screen is set as a query parameter in the URL.
useEffect( () => {
startTransition( () => {
const url = new URL( window.location.href );
const screen = url.searchParams.get( 'screen' );

if ( screen && screen in SCREEN_MAP ) {
setCurrentScreen( screen as AllowedScreens );
// Allowed screens appear in the WPGraphQLLogin.settings global.
if ( screen && isAllowedScreen( screen ) ) {
setCurrentScreen( screen );
}
} );
}, [] );
Expand Down
58 changes: 15 additions & 43 deletions packages/admin/components/screen/screen.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,23 @@
import { __ } from '@wordpress/i18n';
import { Panel, PanelBody, PanelRow } from '@wordpress/components';
import clsx from 'clsx';
import { lazy, Suspense, type PropsWithChildren } from 'react';
import { Loading } from '@/admin/components/ui/loading';
import { useCurrentScreen } from './context';
import { SettingsScreen } from './setting-screen';
import type { AllowedSettingKeys } from '@/admin/types';

import styles from './styles.module.scss';
import { getSettingForScreen } from './utils';
import { __ } from '@wordpress/i18n';

const ClientSettingsScreen = lazy(
() => import( '../provider-config/ClientSettings' )
);

export type AllowedScreens = 'access-control' | 'providers' | 'plugin-settings';

export const SCREEN_MAP: Record< AllowedScreens, AllowedSettingKeys > = {
'access-control': 'wpgraphql_login_access_control',
'plugin-settings': 'wpgraphql_login_settings',
providers: 'providers',
};

/**
* The titles of the screens that are available in the admin.
*
* @todo get from the server.
*/
const SCREEN_TITLES: Record< AllowedScreens, string > = {
'access-control': __(
'Access Control Settings',
'wp-graphql-headless-login'
),
'plugin-settings': __( 'Plugin Settings', 'wp-graphql-headless-login' ),
providers: __( 'Login Providers', 'wp-graphql-headless-login' ),
};

/**
* The descriptions of the screens that are available in the admin.
*
* @todo get from the server.
*/
const SCREEN_DESCRIPTIONS: Record< AllowedScreens, string > = {
'access-control': __(
'Configure the Access Control headers for the plugin.',
'wp-graphql-headless-login'
),
'plugin-settings': __(
'Configure the plugin settings.',
'wp-graphql-headless-login'
),
providers: __(
'Configure the Authentication Providers that are available to users.',
'wp-graphql-headless-login'
),
};

const Wrapper = ( {
title,
Expand Down Expand Up @@ -85,18 +49,26 @@ const Wrapper = ( {
export const Screen = () => {
const { currentScreen } = useCurrentScreen();

const title = SCREEN_TITLES[ currentScreen ];
const description = SCREEN_DESCRIPTIONS[ currentScreen ];
const settingKey = getSettingForScreen( currentScreen );

// @todo get provider context from global.
const title =
wpGraphQLLogin?.settings[ settingKey ]?.title ||
__( 'Login Providers', 'wp-graphql-headless-login' );
const description =
wpGraphQLLogin?.settings[ settingKey ]?.description ||
__(
'Configure the Authentication Providers that are available to users.',
'wp-graphql-headless-login'
);

return (
<Suspense fallback={ <Loading /> }>
<Wrapper title={ title } description={ description }>
{ currentScreen === 'providers' ? (
<ClientSettingsScreen />
) : (
<SettingsScreen
settingKey={ SCREEN_MAP[ currentScreen ] }
/>
<SettingsScreen settingKey={ settingKey } />
) }
</Wrapper>
</Suspense>
Expand Down
10 changes: 3 additions & 7 deletions packages/admin/components/screen/setting-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@ import { useEffect } from 'react';
import { Button, PanelBody, Spinner } from '@wordpress/components';
import { Fields } from '@/admin/components/fields';
import { useSettings } from '@/admin/contexts/settings-context';
import type { AllowedSettingKeys } from '@/admin/types';

export const SettingsScreen = ( {
settingKey,
}: {
settingKey: AllowedSettingKeys;
} ) => {
export const SettingsScreen = ( { settingKey }: { settingKey: string } ) => {
const {
settings: allSettings,
updateSettings,
Expand All @@ -19,7 +14,8 @@ export const SettingsScreen = ( {
errorMessage,
} = useSettings();

const optionsSchema = wpGraphQLLogin?.settings?.[ settingKey ] || undefined;
const optionsSchema =
wpGraphQLLogin?.settings?.[ settingKey ]?.fields || undefined;

const settings = allSettings?.[ settingKey ] || undefined;

Expand Down
47 changes: 47 additions & 0 deletions packages/admin/components/screen/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Maps a plugin setting key to to its corresponding screen.
*
* @param {string} setting The setting to map to a screen. E.g. `wpgraphql_login_access_control`.
*/
export const getScreenForSetting = ( setting: string ): string => {
const settingPrefix = 'wpgraphql_login_';

// First, strip the prefix.
const settingWithoutPrefix = setting.replace( settingPrefix, '' );

// Then, convert to `kebab-case`.
return settingWithoutPrefix.replace( /_/g, '-' );
};

/**
* Maps a screen to it's corresponding plugin setting.
*
* @param {string} screen The screen to map to a setting. E.g. `access-control`.
*/
export const getSettingForScreen = ( screen: string ): string => {
const settingPrefix = 'wpgraphql_login_';

// First, convert to `snake_case`.
const snakeCaseScreen = screen.replace( /-/g, '_' );

// Then, add the prefix.
const setting = settingPrefix + snakeCaseScreen;

// Ensure lowercase
return setting.toLowerCase();
};

/**
* Checks whether a screen is allowed.
*
* Allowed screens are the keys defined in the `WPGraphQLLogin.settings` global.
*
* @param {string} screen The screen to check.
*/
export const isAllowedScreen = ( screen: string ): boolean => {
const allowedSettings = Object.keys( wpGraphQLLogin.settings );

const settingToCheck = getSettingForScreen( screen );

return allowedSettings.includes( settingToCheck );
};
3 changes: 1 addition & 2 deletions packages/admin/contexts/settings-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import {
useState,
} from 'react';
import apiFetch from '@wordpress/api-fetch';
import type { AllowedSettingKeys } from '@/admin/types';

const REST_ENDPOINT = 'wp-graphql-login/v1/settings';

type AllowedStatuses = 'saving' | 'complete' | undefined;

type SettingType = Record< AllowedSettingKeys, Record< string, unknown > >;
type SettingType = Record< string, Record< string, unknown > >;

const SettingsContext = createContext< {
settings: SettingType | undefined;
Expand Down
17 changes: 8 additions & 9 deletions packages/admin/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ declare global {
};
}

type AllowedSettingKeys =
| 'providers'
| 'wpgraphql_login_settings'
| 'wpgraphql_login_access_control';

type AllowedConditionalLogicOperators = '==' | '!=' | '>' | '<' | '>=' | '<=';

type FieldSchema = {
Expand All @@ -45,10 +40,14 @@ type FieldSchema = {
required?: boolean;
};

type SettingSchema = Record<
AllowedSettingKeys,
Record< string, FieldSchema >
>;
type SettingSchema = {
[ key: string ]: {
title: string;
description: string;
label: string;
fields: Record< string, FieldSchema >;
}
};

type ProviderSettingType = {
name: string;
Expand Down
Loading
Loading