From 5b3612c153f5edb0c4c4c49e6df55b75ccee5acd Mon Sep 17 00:00:00 2001 From: Charles Zhao Date: Mon, 27 Jun 2022 20:10:27 +0800 Subject: [PATCH] refactor(console): use single text input in sdk guide on app newly created --- .../docs/tutorial/integrate-sdk/android.mdx | 4 +- .../tutorial/integrate-sdk/android_zh-cn.mdx | 4 +- .../docs/tutorial/integrate-sdk/ios.mdx | 4 +- .../docs/tutorial/integrate-sdk/react.mdx | 6 +- .../tutorial/integrate-sdk/react_zh-cn.mdx | 6 +- .../docs/tutorial/integrate-sdk/vue.mdx | 6 +- .../docs/tutorial/integrate-sdk/vue_zh-cn.mdx | 6 +- .../src/components/TextInput/index.tsx | 10 +- .../MultiTextInputField/index.tsx | 103 ------------- .../index.module.scss | 13 +- .../mdx-components/UriInputField/index.tsx | 135 ++++++++++++++++++ .../Applications/components/Guide/index.tsx | 1 + 12 files changed, 173 insertions(+), 125 deletions(-) delete mode 100644 packages/console/src/mdx-components/MultiTextInputField/index.tsx rename packages/console/src/mdx-components/{MultiTextInputField => UriInputField}/index.module.scss (52%) create mode 100644 packages/console/src/mdx-components/UriInputField/index.tsx diff --git a/packages/console/src/assets/docs/tutorial/integrate-sdk/android.mdx b/packages/console/src/assets/docs/tutorial/integrate-sdk/android.mdx index ca27668e7..f379021e7 100644 --- a/packages/console/src/assets/docs/tutorial/integrate-sdk/android.mdx +++ b/packages/console/src/assets/docs/tutorial/integrate-sdk/android.mdx @@ -1,4 +1,4 @@ -import MultiTextInputField from '@mdx/components/MultiTextInputField'; +import UriInputField from '@mdx/components/UriInputField'; import Step from '@mdx/components/Step'; import Tabs from '@mdx/components/Tabs'; import TabItem from '@mdx/components/TabItem'; @@ -74,7 +74,7 @@ Notes: e.g. `io.logto.android://io.logto.sample/callback` - + ### Configure Logto Android SDK diff --git a/packages/console/src/assets/docs/tutorial/integrate-sdk/android_zh-cn.mdx b/packages/console/src/assets/docs/tutorial/integrate-sdk/android_zh-cn.mdx index c7992d37e..f86241327 100644 --- a/packages/console/src/assets/docs/tutorial/integrate-sdk/android_zh-cn.mdx +++ b/packages/console/src/assets/docs/tutorial/integrate-sdk/android_zh-cn.mdx @@ -1,4 +1,4 @@ -import MultiTextInputField from '@mdx/components/MultiTextInputField'; +import UriInputField from '@mdx/components/UriInputField'; import Step from '@mdx/components/Step'; import Tabs from '@mdx/components/Tabs'; import TabItem from '@mdx/components/TabItem'; @@ -74,7 +74,7 @@ $(LOGTO_REDIRECT_SCHEME)://$(YOUR_APP_PACKAGE)/callback 例: `io.logto.android://io.logto.sample/callback` - + ### 配置 Logto Android SDK diff --git a/packages/console/src/assets/docs/tutorial/integrate-sdk/ios.mdx b/packages/console/src/assets/docs/tutorial/integrate-sdk/ios.mdx index 28526c1fd..bd6973e4a 100644 --- a/packages/console/src/assets/docs/tutorial/integrate-sdk/ios.mdx +++ b/packages/console/src/assets/docs/tutorial/integrate-sdk/ios.mdx @@ -1,4 +1,4 @@ -import MultiTextInputField from '@mdx/components/MultiTextInputField'; +import UriInputField from '@mdx/components/UriInputField'; import Step from '@mdx/components/Step'; import Tabs from '@mdx/components/Tabs'; import TabItem from '@mdx/components/TabItem'; @@ -70,7 +70,7 @@ let config = try? LogtoConfig( First, let’s configure your redirect URI - + ```swift do { diff --git a/packages/console/src/assets/docs/tutorial/integrate-sdk/react.mdx b/packages/console/src/assets/docs/tutorial/integrate-sdk/react.mdx index 9e5310a57..31377639e 100644 --- a/packages/console/src/assets/docs/tutorial/integrate-sdk/react.mdx +++ b/packages/console/src/assets/docs/tutorial/integrate-sdk/react.mdx @@ -1,4 +1,4 @@ -import MultiTextInputField from '@mdx/components/MultiTextInputField'; +import UriInputField from '@mdx/components/UriInputField'; import Step from '@mdx/components/Step'; import Tabs from '@mdx/components/Tabs'; import TabItem from '@mdx/components/TabItem'; @@ -107,7 +107,7 @@ const App = () => { The Logto React SDK provides you tools and hooks to quickly implement your own authorization flow. First, let’s enter your redirect URI - + Add the following code to your web app @@ -157,7 +157,7 @@ const App = () => { Execute signOut() methods will redirect users to the Logto sign out page. After a success sign out, all use session data and auth status will be cleared. - + Add the following code to your web app diff --git a/packages/console/src/assets/docs/tutorial/integrate-sdk/react_zh-cn.mdx b/packages/console/src/assets/docs/tutorial/integrate-sdk/react_zh-cn.mdx index ee166b5b2..df8bd9e11 100644 --- a/packages/console/src/assets/docs/tutorial/integrate-sdk/react_zh-cn.mdx +++ b/packages/console/src/assets/docs/tutorial/integrate-sdk/react_zh-cn.mdx @@ -1,4 +1,4 @@ -import MultiTextInputField from '@mdx/components/MultiTextInputField'; +import UriInputField from '@mdx/components/UriInputField'; import Step from '@mdx/components/Step'; import Tabs from '@mdx/components/Tabs'; import TabItem from '@mdx/components/TabItem'; @@ -108,7 +108,7 @@ const App = () => { The Logto React SDK provides you tools and hooks to quickly implement your own authorization flow. First, let’s enter your redirect URI - + Add the following code to your web app @@ -158,7 +158,7 @@ const App = () => { Execute signOut() methods will redirect users to the Logto sign out page. After a success sign out, all use session data and auth status will be cleared. - + Add the following code to your web app diff --git a/packages/console/src/assets/docs/tutorial/integrate-sdk/vue.mdx b/packages/console/src/assets/docs/tutorial/integrate-sdk/vue.mdx index 44d0f69bb..f0826fd88 100644 --- a/packages/console/src/assets/docs/tutorial/integrate-sdk/vue.mdx +++ b/packages/console/src/assets/docs/tutorial/integrate-sdk/vue.mdx @@ -1,4 +1,4 @@ -import MultiTextInputField from '@mdx/components/MultiTextInputField'; +import UriInputField from '@mdx/components/UriInputField'; import Step from '@mdx/components/Step'; import Tabs from '@mdx/components/Tabs'; import TabItem from '@mdx/components/TabItem'; @@ -83,7 +83,7 @@ In order to handle what comes from Logto, the application needs to have a dedica First, let’s enter your redirect URI. E.g. `http://localhost:1234/callback` - + Then let's create a callback component: @@ -157,7 +157,7 @@ Calling `.signOut()` will clear all the Logto data in memory and LocalStorage, i To make the user come back to your application after signing out, it's necessary to add `http://localhost:1234` as one of the Post Sign Out URIs and use the URL as the parameter when calling `.signOut()`. - + ```ts import { useLogto } from "@logto/vue"; diff --git a/packages/console/src/assets/docs/tutorial/integrate-sdk/vue_zh-cn.mdx b/packages/console/src/assets/docs/tutorial/integrate-sdk/vue_zh-cn.mdx index 1e42798c8..8cc6cffbb 100644 --- a/packages/console/src/assets/docs/tutorial/integrate-sdk/vue_zh-cn.mdx +++ b/packages/console/src/assets/docs/tutorial/integrate-sdk/vue_zh-cn.mdx @@ -1,4 +1,4 @@ -import MultiTextInputField from '@mdx/components/MultiTextInputField'; +import UriInputField from '@mdx/components/UriInputField'; import Step from '@mdx/components/Step'; import Tabs from '@mdx/components/Tabs'; import TabItem from '@mdx/components/TabItem'; @@ -83,7 +83,7 @@ app.mount("#app"); 但首先, 让我们先在下方输入 redirect URI,如:`http://localhost:1234/callback` - + 然后,让我们来创建一个 CallbackView 组件: @@ -157,7 +157,7 @@ const { isAuthenticated } = useLogto(); 为了确保用户登出后能够跳转回你的应用,我们需要首先在管理界面中将 `http://localhost:1234` 添加到允许登出后跳转的地址列表(Post Sign Out URIs)中。 - + ```ts import { useLogto } from "@logto/vue"; diff --git a/packages/console/src/components/TextInput/index.tsx b/packages/console/src/components/TextInput/index.tsx index 844dc802e..7de0bccb1 100644 --- a/packages/console/src/components/TextInput/index.tsx +++ b/packages/console/src/components/TextInput/index.tsx @@ -10,7 +10,15 @@ type Props = HTMLProps & { }; const TextInput = ( - { hasError = false, errorMessage, icon, disabled, className, readOnly, ...rest }: Props, + { + errorMessage, + hasError = Boolean(errorMessage), + icon, + disabled, + className, + readOnly, + ...rest + }: Props, reference: ForwardedRef ) => { return ( diff --git a/packages/console/src/mdx-components/MultiTextInputField/index.tsx b/packages/console/src/mdx-components/MultiTextInputField/index.tsx deleted file mode 100644 index 9963442b2..000000000 --- a/packages/console/src/mdx-components/MultiTextInputField/index.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { I18nKey } from '@logto/phrases'; -import { Application } from '@logto/schemas'; -import React, { useRef } from 'react'; -import { Controller, FormProvider, useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import useSWR from 'swr'; - -import Button from '@/components/Button'; -import FormField from '@/components/FormField'; -import MultiTextInput from '@/components/MultiTextInput'; -import { createValidatorForRhf, convertRhfErrorMessage } from '@/components/MultiTextInput/utils'; -import useApi, { RequestError } from '@/hooks/use-api'; -import { GuideForm } from '@/types/guide'; -import { uriValidator } from '@/utilities/validator'; - -import * as styles from './index.module.scss'; - -type Props = { - appId: string; - name: 'redirectUris' | 'postLogoutRedirectUris'; - title: I18nKey; -}; - -const MultiTextInputField = ({ appId, name, title }: Props) => { - const methods = useForm(); - const { - control, - getValues, - handleSubmit, - reset, - formState: { isSubmitting }, - } = methods; - - const { data, mutate } = useSWR(`/api/applications/${appId}`); - - const ref = useRef(null); - const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); - const api = useApi(); - - const onSubmit = async (value: string[]) => { - const updatedApp = await api - .patch(`/api/applications/${appId}`, { - json: { - oidcClientMetadata: { - [name]: value.filter(Boolean), - }, - }, - }) - .json(); - void mutate(updatedApp); - - // Reset form to set 'isDirty' to false - reset(getValues()); - }; - - return ( - -
- - !value || uriValidator(value), - message: t('errors.invalid_uri_format'), - }, - }), - }} - render={({ field: { onChange, value }, fieldState: { error, isDirty } }) => ( -
- { - if (event.key === 'Enter') { - event.preventDefault(); - void handleSubmit(async () => onSubmit(value))(); - } - }} - /> -
- )} - /> -
-
-
- ); -}; - -export default MultiTextInputField; diff --git a/packages/console/src/mdx-components/MultiTextInputField/index.module.scss b/packages/console/src/mdx-components/UriInputField/index.module.scss similarity index 52% rename from packages/console/src/mdx-components/MultiTextInputField/index.module.scss rename to packages/console/src/mdx-components/UriInputField/index.module.scss index a6872787b..becb32f77 100644 --- a/packages/console/src/mdx-components/MultiTextInputField/index.module.scss +++ b/packages/console/src/mdx-components/UriInputField/index.module.scss @@ -1,10 +1,17 @@ @use '@/scss/underscore' as _; +.wrapper { + display: flex; + align-items: flex-start; + position: relative; +} + .field { width: 556px; } -.wrapper { - display: flex; - align-items: flex-start; +.saveButton { + position: absolute; + left: calc(556px + _.unit(3)); + top: 0; } diff --git a/packages/console/src/mdx-components/UriInputField/index.tsx b/packages/console/src/mdx-components/UriInputField/index.tsx new file mode 100644 index 000000000..7d1b40a95 --- /dev/null +++ b/packages/console/src/mdx-components/UriInputField/index.tsx @@ -0,0 +1,135 @@ +import { I18nKey } from '@logto/phrases'; +import { Application } from '@logto/schemas'; +import React, { useRef, KeyboardEvent } from 'react'; +import { Controller, FormProvider, useForm } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; +import useSWR from 'swr'; + +import Button from '@/components/Button'; +import FormField from '@/components/FormField'; +import MultiTextInput from '@/components/MultiTextInput'; +import { convertRhfErrorMessage, createValidatorForRhf } from '@/components/MultiTextInput/utils'; +import TextInput from '@/components/TextInput'; +import useApi, { RequestError } from '@/hooks/use-api'; +import { GuideForm } from '@/types/guide'; +import { uriValidator } from '@/utilities/validator'; + +import * as styles from './index.module.scss'; + +type Props = { + appId: string; + name: 'redirectUris' | 'postLogoutRedirectUris'; + title: I18nKey; + isSingle?: boolean; +}; + +const UriInputField = ({ appId, name, title, isSingle = false }: Props) => { + const methods = useForm>(); + const { + control, + getValues, + handleSubmit, + reset, + formState: { isSubmitting }, + } = methods; + + const { data, mutate } = useSWR(`/api/applications/${appId}`); + + const ref = useRef(null); + const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' }); + const api = useApi(); + + const onSubmit = async (value: string[]) => { + const updatedApp = await api + .patch(`/api/applications/${appId}`, { + json: { + oidcClientMetadata: { + [name]: value.filter(Boolean), + }, + }, + }) + .json(); + void mutate(updatedApp); + + // Reset form to set 'isDirty' to false + reset(getValues()); + }; + + const onKeyPress = (event: KeyboardEvent, value: string[]) => { + if (event.key === 'Enter') { + event.preventDefault(); + void handleSubmit(async () => onSubmit(value))(); + } + }; + + return ( + +
+ + !value || uriValidator(value), + message: t('errors.invalid_uri_format'), + }, + }), + }} + render={({ field: { onChange, value = [] }, fieldState: { error, isDirty } }) => { + const errorObject = convertRhfErrorMessage(error?.message); + + return ( +
+ {isSingle && ( + { + onChange([value]); + }} + onKeyPress={(event) => { + onKeyPress(event, value); + }} + /> + )} + {!isSingle && ( + { + onKeyPress(event, value); + }} + /> + )} +
+ ); + }} + /> +
+
+
+ ); +}; + +export default UriInputField; diff --git a/packages/console/src/pages/Applications/components/Guide/index.tsx b/packages/console/src/pages/Applications/components/Guide/index.tsx index 19b5bcb64..316dc0dee 100644 --- a/packages/console/src/pages/Applications/components/Guide/index.tsx +++ b/packages/console/src/pages/Applications/components/Guide/index.tsx @@ -71,6 +71,7 @@ const Guide = ({ app, isCompact, onClose }: Props) => { { setActiveStepIndex(nextIndex); }}