diff --git a/src/views/credentials/Credentials.js b/src/views/credentials/Credentials.js index 9c0e665a00..3f23624cad 100644 --- a/src/views/credentials/Credentials.js +++ b/src/views/credentials/Credentials.js @@ -1,5 +1,6 @@ import React, { Fragment, + useMemo, useRef, useState, useEffect, @@ -112,9 +113,13 @@ export const Credentials = React.forwardRef( const tokens = useTokens() const styles = getStyles(tokens, isSmall) const getNextDelay = getDelay(0, 100) - const initialValues = buildInitialValues(credentials) - const formSchema = buildFormSchema(credentials) - const loginFieldCount = credentials.length + const sortedCredentials = useMemo( + () => [...credentials].sort((a, b) => (a.display_order ?? Infinity) - (b.display_order ?? Infinity)), + [credentials], + ) + const initialValues = buildInitialValues(sortedCredentials) + const formSchema = buildFormSchema(sortedCredentials) + const loginFieldCount = sortedCredentials.length const showDisconnectOption = currentMember && currentMember.is_managed_by_user && @@ -249,7 +254,7 @@ export const Credentials = React.forwardRef( const inputRefs = useRef({}) useEffect(() => { - for (const field of credentials) { + for (const field of sortedCredentials) { if (errors[field.field_name]) { inputRefs.current[field.field_name]?.focus() break @@ -258,7 +263,7 @@ export const Credentials = React.forwardRef( }, [errors]) function attemptConnect() { - const credentialsPayload = credentials.map((credential) => { + const credentialsPayload = sortedCredentials.map((credential) => { return { guid: credential.guid, value: values[credential.field_name], @@ -443,7 +448,7 @@ export const Credentials = React.forwardRef( onSubmit={(e) => e.preventDefault()} style={styles.form} > - {credentials.map((field) => ( + {sortedCredentials.map((field) => ( {field.field_type === CREDENTIAL_FIELD_TYPES.PASSWORD ? (
diff --git a/src/views/credentials/__tests__/Credentials-test.tsx b/src/views/credentials/__tests__/Credentials-test.tsx index 30b629f6fd..546f2ad7f1 100644 --- a/src/views/credentials/__tests__/Credentials-test.tsx +++ b/src/views/credentials/__tests__/Credentials-test.tsx @@ -140,6 +140,71 @@ describe('Credentials', () => { expect(screen.getByText('Data access by')).toBeInTheDocument() }) }) + it('renders credentials in display_order regardless of API response order', async () => { + const reversedCredentialProps = { + ...credentialProps, + credentials: [ + { + guid: 'CRD-456', + label: 'Password', + field_name: 'password', + field_type: 1, + display_order: 2, + }, + { + guid: 'CRD-123', + label: 'Username', + field_name: 'username', + field_type: 3, + display_order: 1, + }, + ], + } + const ref = React.createRef() + render(, { + preloadedState: initialStateCopy, + }) + + const usernameField = await screen.findByLabelText(/Enter your Username/i) + const passwordField = await screen.findByLabelText(/Password/i) + const passwordFollowsUsername = + usernameField.compareDocumentPosition(passwordField) & + Node.DOCUMENT_POSITION_FOLLOWING + expect(passwordFollowsUsername).toBeTruthy() + }) + + it('sorts credentials without display_order to the end', async () => { + const mixedCredentialProps = { + ...credentialProps, + credentials: [ + { + guid: 'CRD-789', + label: 'PIN', + field_name: 'pin', + field_type: 3, + }, + { + guid: 'CRD-123', + label: 'Username', + field_name: 'username', + field_type: 3, + display_order: 1, + }, + ], + } + const ref = React.createRef() + render(, { + preloadedState: initialStateCopy, + }) + + const usernameField = await screen.findByLabelText(/Enter your Username/i) + const pinField = await screen.findByLabelText(/Enter your PIN/i) + const pinFollowsUsername = + usernameField.compareDocumentPosition(pinField) & + Node.DOCUMENT_POSITION_FOLLOWING + expect(pinFollowsUsername).toBeTruthy() + }) + it('renders credentials and makes sure that the powered by MX footer is not present', () => { const ref = React.createRef() render(, { preloadedState: initialStateCopy })