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

feat(console): add global loading skeleton #5498

Merged
merged 1 commit into from
Mar 14, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* This context is used to share the loading state of the code editor with the root page.
*
* 1. First code editor won't be rendered until the fetch API is returned.
* 2. After the code editor is mounted, multiple async operations will be triggered to load the monaco editor.
* 3. We need to mount the code editor component will keep the loading state until the monaco editor is ready.
*/

import { noop } from '@silverhand/essentials';
import { createContext } from 'react';

type CodeEditorLoadingContextType = {
isMonacoLoaded: boolean;
setIsMonacoLoaded: (isLoading: boolean) => void;
};

export const CodeEditorLoadingContext = createContext<CodeEditorLoadingContextType>({
isMonacoLoaded: false,
setIsMonacoLoaded: noop,
});
4 changes: 3 additions & 1 deletion packages/console/src/pages/JwtClaims/Main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { type JwtClaimsFormType } from './type';
import { formatResponseDataToFormData, formatFormDataToRequestData, getApiPath } from './utils';

type Props = {
className?: string;
tab: LogtoJwtTokenPath;
accessTokenJwtCustomizer: AccessTokenJwtCustomizer | undefined;
clientCredentialsJwtCustomizer: ClientCredentialsJwtCustomizer | undefined;
Expand All @@ -28,6 +29,7 @@ type Props = {
};

function Main({
className,
tab,
accessTokenJwtCustomizer,
clientCredentialsJwtCustomizer,
Expand Down Expand Up @@ -87,7 +89,7 @@ function Main({
return (
<>
<FormProvider {...activeForm}>
<form className={classNames(styles.tabContent)}>
<form className={classNames(styles.tabContent, className)}>
<ScriptSection />
<SettingsSection />
</form>
Expand Down
14 changes: 10 additions & 4 deletions packages/console/src/pages/JwtClaims/MonacoCodeEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Props = {
setActiveModel?: (name: string) => void;
value?: string;
onChange?: (value: string | undefined) => void;
onMountHandler?: (editor: IStandaloneCodeEditor) => void;
};
/**
* Monaco code editor component.
Expand All @@ -47,6 +48,7 @@ function MonacoCodeEditor({
value,
setActiveModel,
onChange,
onMountHandler,
}: Props) {
const monaco = useMonaco();
const editorRef = useRef<Nullable<IStandaloneCodeEditor>>(null);
Expand Down Expand Up @@ -84,10 +86,14 @@ function MonacoCodeEditor({
monaco.editor.defineTheme('logto-dark', logtoDarkTheme);
}, []);

const handleEditorDidMount = useCallback<OnMount>((editor) => {
// eslint-disable-next-line @silverhand/fp/no-mutation
editorRef.current = editor;
}, []);
const handleEditorDidMount = useCallback<OnMount>(
(editor) => {
// eslint-disable-next-line @silverhand/fp/no-mutation
editorRef.current = editor;
onMountHandler?.(editor);
},
[onMountHandler]
);

return (
<div className={classNames(className, styles.codeEditor)}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@use '@/scss/underscore' as _;

.blockShimmer {
@include _.shimmering-animation;
border-radius: 8px;
flex: 1;
}

.card:not(:last-child) {
margin-bottom: _.unit(4);
}


.textShimmer {
@include _.shimmering-animation;
width: 100%;
height: _.unit(6);
border-radius: 8px;

&:not(:last-child) {
margin-bottom: _.unit(4);
}

&.title {
width: _.unit(30);
}

&.large {
height: _.unit(8);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { LogtoJwtTokenPath } from '@logto/schemas';
import classNames from 'classnames';

import Card from '@/ds-components/Card';

import * as pageLayoutStyles from '../index.module.scss';

import * as styles from './index.module.scss';

type Props = {
tokenType: LogtoJwtTokenPath;
};

function PageLoadingSkeleton({ tokenType }: Props) {
return (
<div className={pageLayoutStyles.tabContent}>
<Card className={pageLayoutStyles.codePanel}>
<div className={classNames(styles.textShimmer, styles.title)} />
<div className={styles.blockShimmer} />
</Card>
<div>
<div className={classNames(styles.textShimmer, styles.large)} />
<Card className={styles.card}>
<div className={classNames(styles.textShimmer, styles.title)} />
<div className={styles.textShimmer} />
</Card>
<Card className={styles.card}>
<div className={classNames(styles.textShimmer, styles.title)} />
<div className={styles.textShimmer} />
</Card>
<Card className={styles.card}>
<div className={classNames(styles.textShimmer, styles.title)} />
<div className={styles.textShimmer} />
</Card>
{tokenType === LogtoJwtTokenPath.AccessToken && (
<Card className={styles.card}>
<div className={styles.textShimmer} />
<div className={styles.textShimmer} />
</Card>
)}
</div>
</div>
);
}

export default PageLoadingSkeleton;
10 changes: 9 additions & 1 deletion packages/console/src/pages/JwtClaims/ScriptSection.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/* Code Editor for the custom JWT claims script. */
import { LogtoJwtTokenPath } from '@logto/schemas';
import { useMemo } from 'react';
import { useMemo, useContext, useCallback } from 'react';
import { useFormContext, Controller } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import Card from '@/ds-components/Card';

import { CodeEditorLoadingContext } from './CodeEditorLoadingContext';
import MonacoCodeEditor, { type ModelSettings } from './MonacoCodeEditor';
import { userJwtFile, machineToMachineJwtFile } from './config';
import * as styles from './index.module.scss';
Expand All @@ -22,11 +23,17 @@ function ScriptSection() {
const { watch, control } = useFormContext<JwtClaimsFormType>();
const tokenType = watch('tokenType');

const { setIsMonacoLoaded } = useContext(CodeEditorLoadingContext);

const activeModel = useMemo<ModelSettings>(
() => (tokenType === LogtoJwtTokenPath.AccessToken ? userJwtFile : machineToMachineJwtFile),
[tokenType]
);

const onMountHandler = useCallback(() => {
setIsMonacoLoaded(true);
}, [setIsMonacoLoaded]);

return (
<Card className={styles.codePanel}>
<div className={styles.cardTitle}>
Expand Down Expand Up @@ -56,6 +63,7 @@ function ScriptSection() {

onChange(newValue);
}}
onMountHandler={onMountHandler}
/>
)}
/>
Expand Down
4 changes: 4 additions & 0 deletions packages/console/src/pages/JwtClaims/index.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@
flex-grow: 1;
}
}

.hidden {
display: none;
}
18 changes: 16 additions & 2 deletions packages/console/src/pages/JwtClaims/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { withAppInsights } from '@logto/app-insights/react/AppInsightsReact';
import { LogtoJwtTokenPath } from '@logto/schemas';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';

import CardTitle from '@/ds-components/CardTitle';
import TabNav, { TabNavItem } from '@/ds-components/TabNav';

import { CodeEditorLoadingContext } from './CodeEditorLoadingContext';
import Main from './Main';
import PageLoadingSkeleton from './PageLoadingSkeleton';
import * as styles from './index.module.scss';
import useJwtCustomizer from './use-jwt-customizer';

Expand All @@ -24,6 +27,12 @@ function JwtClaims({ tab }: Props) {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });

const { isLoading, ...rest } = useJwtCustomizer();
const [isMonacoLoaded, setIsMonacoLoaded] = useState(false);

const codeEditorContextValue = useMemo(
() => ({ isMonacoLoaded, setIsMonacoLoaded }),
[isMonacoLoaded]
);

return (
<div className={styles.container}>
Expand All @@ -39,8 +48,13 @@ function JwtClaims({ tab }: Props) {
</TabNavItem>
))}
</TabNav>
{/* TODO: Loading skelton */}
{!isLoading && <Main tab={tab} {...rest} />}
{(isLoading || !isMonacoLoaded) && <PageLoadingSkeleton tokenType={tab} />}

{!isLoading && (
<CodeEditorLoadingContext.Provider value={codeEditorContextValue}>
<Main tab={tab} {...rest} className={isMonacoLoaded ? undefined : styles.hidden} />
</CodeEditorLoadingContext.Provider>
)}
</div>
);
}
Expand Down
Loading