From b68142cfa5e4f3c73c0138a2f6001f2d8dd671cf Mon Sep 17 00:00:00 2001 From: Awais Ansari <79941147+awais-ansari@users.noreply.github.com> Date: Wed, 5 May 2021 18:26:29 +0500 Subject: [PATCH] style: update piazza config form according to hifi design and add action test case (#88) --- .../discussions/DiscussionsSettings.jsx | 4 +- .../apps/lti/LtiConfigForm.jsx | 41 +++++++++++-------- .../app-config-form/apps/lti/messages.js | 6 +-- .../discussions/data/redux.test.js | 18 +++++++- .../discussions/data/slice.js | 7 ++++ .../discussions/messages.js | 5 +++ 6 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/pages-and-resources/discussions/DiscussionsSettings.jsx b/src/pages-and-resources/discussions/DiscussionsSettings.jsx index c6b076447..500b07c31 100644 --- a/src/pages-and-resources/discussions/DiscussionsSettings.jsx +++ b/src/pages-and-resources/discussions/DiscussionsSettings.jsx @@ -30,7 +30,7 @@ const SETTINGS_STEP = 'settings'; function DiscussionsSettings({ courseId, intl }) { const dispatch = useDispatch(); const { path: pagesAndResourcesPath } = useContext(PagesAndResourcesContext); - const { status } = useSelector(state => state.discussions); + const { status, hasValidationError } = useSelector(state => state.discussions); useEffect(() => { dispatch(fetchApps(courseId)); @@ -119,6 +119,8 @@ function DiscussionsSettings({ courseId, intl }) { { + dispatch(updateValidationStatus({ hasError: Object.keys(errors).length > 0 })); + }, [errors]); + return ( - +
-

{title}

- -

+

{title}

+

- - {intl.formatMessage(messages.consumerKey)} + {isInvalidConsumerKey && ( - - {errors.consumerKey} + + {errors.consumerKey} )} - - {intl.formatMessage(messages.consumerSecret)} + {isInvalidConsumerSecret && ( - - {errors.consumerSecret} + + {errors.consumerSecret} )} - {intl.formatMessage(messages.launchUrl)} {isInvalidLaunchUrl && ( - - {errors.launchUrl} + + {errors.launchUrl} )} diff --git a/src/pages-and-resources/discussions/app-config-form/apps/lti/messages.js b/src/pages-and-resources/discussions/app-config-form/apps/lti/messages.js index fba37ceea..2f20eabe5 100644 --- a/src/pages-and-resources/discussions/app-config-form/apps/lti/messages.js +++ b/src/pages-and-resources/discussions/app-config-form/apps/lti/messages.js @@ -12,7 +12,7 @@ const messages = defineMessages({ }, consumerKeyRequired: { id: 'authoring.discussions.consumerKey.required', - defaultMessage: 'Consumer Key is a required field.', + defaultMessage: 'Consumer key is a required field', description: 'Tells the user that the Consumer Key field is required and must have a value.', }, consumerSecret: { @@ -22,7 +22,7 @@ const messages = defineMessages({ }, consumerSecretRequired: { id: 'authoring.discussions.consumerSecret.required', - defaultMessage: 'Consumer Secret is a required field.', + defaultMessage: 'Consumer secret is a required field', description: 'Tells the user that the Consumer Secret field is required and must have a value.', }, launchUrl: { @@ -32,7 +32,7 @@ const messages = defineMessages({ }, launchUrlRequired: { id: 'authoring.discussions.launchUrl.required', - defaultMessage: 'Launch URL is a required field.', + defaultMessage: 'Launch URL is a required field', description: 'Tells the user that the Launch URL field is required and must have a value.', }, }); diff --git a/src/pages-and-resources/discussions/data/redux.test.js b/src/pages-and-resources/discussions/data/redux.test.js index 4fd624d9d..8392b35fb 100644 --- a/src/pages-and-resources/discussions/data/redux.test.js +++ b/src/pages-and-resources/discussions/data/redux.test.js @@ -5,7 +5,7 @@ import { history } from '@edx/frontend-platform'; import initializeStore from '../../../store'; import { getAppsUrl } from './api'; import { - FAILED, SAVED, DENIED, selectApp, + FAILED, SAVED, DENIED, selectApp, updateValidationStatus, } from './slice'; import { fetchApps, saveAppConfig } from './thunks'; import { LOADED } from '../../../data/slice'; @@ -102,6 +102,7 @@ describe('Data layer integration tests', () => { selectedAppId: null, status: FAILED, saveStatus: SAVED, + hasValidationError: false, }), ); }); @@ -119,6 +120,7 @@ describe('Data layer integration tests', () => { selectedAppId: null, status: DENIED, saveStatus: SAVED, + hasValidationError: false, }), ); }); @@ -135,6 +137,7 @@ describe('Data layer integration tests', () => { selectedAppId: null, status: LOADED, saveStatus: SAVED, + hasValidationError: false, }); expect(store.getState().models.apps.legacy).toEqual(legacyApp); expect(store.getState().models.apps.piazza).toEqual(piazzaApp); @@ -159,6 +162,7 @@ describe('Data layer integration tests', () => { selectedAppId: null, status: LOADED, saveStatus: SAVED, + hasValidationError: false, }); expect(store.getState().models.apps.legacy).toEqual(legacyApp); expect(store.getState().models.apps.piazza).toEqual(piazzaApp); @@ -188,6 +192,14 @@ describe('Data layer integration tests', () => { }); }); + describe('updateValidationStatus', () => { + test.each([true, false])('sets hasValidationError value to %s ', (hasError) => { + store.dispatch(updateValidationStatus({ hasError })); + + expect(store.getState().discussions.hasValidationError).toEqual(hasError); + }); + }); + describe('saveAppConfig', () => { test('network error', async () => { history.push(`/course/${courseId}/pages-and-resources/discussions/configure/piazza`); @@ -210,6 +222,7 @@ describe('Data layer integration tests', () => { selectedAppId: 'piazza', status: LOADED, saveStatus: FAILED, + hasValidationError: false, }), ); }); @@ -235,6 +248,7 @@ describe('Data layer integration tests', () => { selectedAppId: 'piazza', status: DENIED, // We set BOTH statuses to DENIED for saveAppConfig - this removes the UI. saveStatus: DENIED, + hasValidationError: false, }), ); }); @@ -288,6 +302,7 @@ describe('Data layer integration tests', () => { selectedAppId: 'piazza', status: LOADED, saveStatus: SAVED, + hasValidationError: false, }), ); expect(store.getState().models.appConfigs.piazza).toEqual({ @@ -352,6 +367,7 @@ describe('Data layer integration tests', () => { selectedAppId: 'legacy', status: LOADED, saveStatus: SAVED, + hasValidationError: false, }), ); expect(store.getState().models.appConfigs.legacy).toEqual({ diff --git a/src/pages-and-resources/discussions/data/slice.js b/src/pages-and-resources/discussions/data/slice.js index c10b4af0d..49092f150 100644 --- a/src/pages-and-resources/discussions/data/slice.js +++ b/src/pages-and-resources/discussions/data/slice.js @@ -21,6 +21,8 @@ const slice = createSlice({ selectedAppId: null, status: LOADING, saveStatus: SAVED, + // ValidationError is the Flag that represents a form validation status. + hasValidationError: false, }, reducers: { loadApps: (state, { payload }) => { @@ -42,6 +44,10 @@ const slice = createSlice({ const { status } = payload; state.saveStatus = status; }, + updateValidationStatus: (state, { payload }) => { + const { hasError } = payload; + state.hasValidationError = hasError; + }, }, }); @@ -50,6 +56,7 @@ export const { selectApp, updateStatus, updateSaveStatus, + updateValidationStatus, } = slice.actions; export const { diff --git a/src/pages-and-resources/discussions/messages.js b/src/pages-and-resources/discussions/messages.js index b7ea0d55c..163bd1513 100644 --- a/src/pages-and-resources/discussions/messages.js +++ b/src/pages-and-resources/discussions/messages.js @@ -39,6 +39,11 @@ const messages = defineMessages({ defaultMessage: 'Provider selection', description: 'A label for the first step of a wizard where the user chooses a discussion tool to configure.', }, + Incomplete: { + id: 'authoring.discussions.Incomplete', + defaultMessage: 'Incomplete', + description: 'A description for the second step of the app configuration stepper.', + }, }); export default messages;