Update step names, form titles, and add dividers (#75)
* fix: adjusting discussion selection step 1 message Removing a duplicate message too. * fix: adding a dividing line between all sections of the app config forms The line is its own component because it has some custom styling to make it go edge-to-edge. There’s a known issue here where a divider inside a TransitionReplace component won’t show its full width until the transition is finished. No idea how to fix that. * fix: moving app titles inside the config forms. Also moving the Card styling in too, since it sorta goes with the dividers and the overall look of the specific forms moreso than the parent. * fix: allowing dividers to be thicker between sections
This commit is contained in:
@@ -31,7 +31,7 @@ function DiscussionsSettings({ courseId, intl }) {
|
||||
const isFirstStep = pathname === discussionsPath;
|
||||
|
||||
const steps = [{
|
||||
label: intl.formatMessage(messages.selectDiscussionTool),
|
||||
label: intl.formatMessage(messages.providerSelection),
|
||||
iconLabel: isFirstStep ? undefined : (
|
||||
<Check style={{ width: '1rem', height: '1rem' }} />
|
||||
),
|
||||
|
||||
@@ -4,7 +4,7 @@ import React, {
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useRouteMatch } from 'react-router';
|
||||
import { Card, Container } from '@edx/paragon';
|
||||
import { Container } from '@edx/paragon';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { history } from '@edx/frontend-platform';
|
||||
import { useModel } from '../../../generic/model-store';
|
||||
@@ -52,6 +52,7 @@ function AppConfigForm({
|
||||
formRef={formRef}
|
||||
appConfig={appConfig}
|
||||
onSubmit={handleSubmit}
|
||||
title={intl.formatMessage(messages[`appName-${app.id}`])}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
@@ -61,17 +62,13 @@ function AppConfigForm({
|
||||
app={app}
|
||||
appConfig={appConfig}
|
||||
onSubmit={handleSubmit}
|
||||
title={intl.formatMessage(messages[`appName-${app.id}`])}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Container size="sm" className="px-sm-0">
|
||||
<h3 className="my-4">
|
||||
{intl.formatMessage(messages.configureApp, { name: app.name })}
|
||||
</h3>
|
||||
<Card className="mb-5 p-5">
|
||||
{form}
|
||||
</Card>
|
||||
<Container size="sm" className="px-sm-0 py-5">
|
||||
{form}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Form } from '@edx/paragon';
|
||||
import { Card, Form } from '@edx/paragon';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
@@ -10,9 +10,10 @@ import AnonymousPostingFields from '../shared/AnonymousPostingFields';
|
||||
import BlackoutDatesField, { blackoutDatesRegex } from '../shared/BlackoutDatesField';
|
||||
|
||||
import messages from '../shared/messages';
|
||||
import AppConfigFormDivider from '../shared/AppConfigFormDivider';
|
||||
|
||||
function LegacyConfigForm({
|
||||
appConfig, onSubmit, formRef, intl,
|
||||
appConfig, onSubmit, formRef, intl, title,
|
||||
}) {
|
||||
const {
|
||||
handleSubmit,
|
||||
@@ -31,29 +32,31 @@ function LegacyConfigForm({
|
||||
onSubmit,
|
||||
});
|
||||
|
||||
const divider = <hr className="my-2" />;
|
||||
|
||||
return (
|
||||
<Form ref={formRef} onSubmit={handleSubmit}>
|
||||
<h3>Legacy Discussions</h3>
|
||||
<DivisionByGroupFields
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
values={values}
|
||||
/>
|
||||
{divider}
|
||||
<AnonymousPostingFields
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
values={values}
|
||||
/>
|
||||
<BlackoutDatesField
|
||||
errors={errors}
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
values={values}
|
||||
/>
|
||||
</Form>
|
||||
<Card className="mb-5 pt-3 px-5 pb-5">
|
||||
<Form ref={formRef} onSubmit={handleSubmit}>
|
||||
<h3>{title}</h3>
|
||||
<AppConfigFormDivider />
|
||||
<DivisionByGroupFields
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
values={values}
|
||||
/>
|
||||
<AppConfigFormDivider thick />
|
||||
<AnonymousPostingFields
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
values={values}
|
||||
/>
|
||||
<AppConfigFormDivider thick />
|
||||
<BlackoutDatesField
|
||||
errors={errors}
|
||||
onBlur={handleBlur}
|
||||
onChange={handleChange}
|
||||
values={values}
|
||||
/>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,6 +75,7 @@ LegacyConfigForm.propTypes = {
|
||||
// eslint-disable-next-line react/forbid-prop-types
|
||||
formRef: PropTypes.object.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(LegacyConfigForm);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Form, Hyperlink } from '@edx/paragon';
|
||||
import { Card, Form, Hyperlink } from '@edx/paragon';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
|
||||
import messages from './messages';
|
||||
import AppConfigFormDivider from '../shared/AppConfigFormDivider';
|
||||
|
||||
function LtiConfigForm({
|
||||
appConfig, app, onSubmit, intl, formRef,
|
||||
appConfig, app, onSubmit, intl, formRef, title,
|
||||
}) {
|
||||
const {
|
||||
handleSubmit,
|
||||
@@ -27,66 +28,70 @@ function LtiConfigForm({
|
||||
});
|
||||
|
||||
return (
|
||||
<Form ref={formRef} onSubmit={handleSubmit}>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="authoring.discussions.appDocInstructions"
|
||||
defaultMessage="{documentationPageLink} to set up the tool, then paste your consumer key and consumer secret below:"
|
||||
description="Instructions for the user to go visit a third party app's documentation to learn how to generate a set of values needed in this form. documentationPageLink says 'Visit the {name} documentation page'"
|
||||
values={{
|
||||
documentationPageLink: (
|
||||
<Hyperlink
|
||||
destination={app.documentationUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{intl.formatMessage(messages.documentationPage, { name: app.name })}
|
||||
</Hyperlink>
|
||||
),
|
||||
name: app.name,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<Form.Group controlId="consumerKey" isInvalid={errors.consumerKey}>
|
||||
<Form.Label>{intl.formatMessage(messages.consumerKey)}</Form.Label>
|
||||
<Form.Control
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.consumerKey}
|
||||
/>
|
||||
{errors.consumerKey && (
|
||||
<Card className="mb-5 pt-3 px-5 pb-5">
|
||||
<Form ref={formRef} onSubmit={handleSubmit}>
|
||||
<h3>{title}</h3>
|
||||
<AppConfigFormDivider />
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="authoring.discussions.appDocInstructions"
|
||||
defaultMessage="{documentationPageLink} to set up the tool, then paste your consumer key and consumer secret below:"
|
||||
description="Instructions for the user to go visit a third party app's documentation to learn how to generate a set of values needed in this form. documentationPageLink says 'Visit the {name} documentation page'"
|
||||
values={{
|
||||
documentationPageLink: (
|
||||
<Hyperlink
|
||||
destination={app.documentationUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{intl.formatMessage(messages.documentationPage, { name: app.name })}
|
||||
</Hyperlink>
|
||||
),
|
||||
name: app.name,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<Form.Group controlId="consumerKey" isInvalid={errors.consumerKey}>
|
||||
<Form.Label>{intl.formatMessage(messages.consumerKey)}</Form.Label>
|
||||
<Form.Control
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.consumerKey}
|
||||
/>
|
||||
{errors.consumerKey && (
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.consumerKey}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</Form.Group>
|
||||
<Form.Group controlId="consumerSecret" isInvalid={errors.consumerSecret}>
|
||||
<Form.Label>{intl.formatMessage(messages.consumerSecret)}</Form.Label>
|
||||
<Form.Control
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.consumerSecret}
|
||||
/>
|
||||
{errors.consumerSecret && (
|
||||
)}
|
||||
</Form.Group>
|
||||
<Form.Group controlId="consumerSecret" isInvalid={errors.consumerSecret}>
|
||||
<Form.Label>{intl.formatMessage(messages.consumerSecret)}</Form.Label>
|
||||
<Form.Control
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.consumerSecret}
|
||||
/>
|
||||
{errors.consumerSecret && (
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.consumerSecret}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</Form.Group>
|
||||
<Form.Group controlId="launchUrl" isInvalid={errors.launchUrl}>
|
||||
<Form.Label>{intl.formatMessage(messages.launchUrl)}</Form.Label>
|
||||
<Form.Control
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.launchUrl}
|
||||
/>
|
||||
{errors.launchUrl && (
|
||||
)}
|
||||
</Form.Group>
|
||||
<Form.Group controlId="launchUrl" isInvalid={errors.launchUrl}>
|
||||
<Form.Label>{intl.formatMessage(messages.launchUrl)}</Form.Label>
|
||||
<Form.Control
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.launchUrl}
|
||||
/>
|
||||
{errors.launchUrl && (
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{errors.launchUrl}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</Form.Group>
|
||||
</Form>
|
||||
)}
|
||||
</Form.Group>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -105,6 +110,7 @@ LtiConfigForm.propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
// eslint-disable-next-line react/forbid-prop-types
|
||||
formRef: PropTypes.object.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(LtiConfigForm);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { TransitionReplace } from '@edx/paragon';
|
||||
import FormSwitchGroup from '../../../../../generic/FormSwitchGroup';
|
||||
import messages from './messages';
|
||||
import AppConfigFormDivider from './AppConfigFormDivider';
|
||||
|
||||
function AnonymousPostingFields({
|
||||
onBlur,
|
||||
@@ -25,6 +26,7 @@ function AnonymousPostingFields({
|
||||
<TransitionReplace>
|
||||
{values.allowAnonymousPosts ? (
|
||||
<React.Fragment key="open">
|
||||
<AppConfigFormDivider />
|
||||
<FormSwitchGroup
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default function AppConfigFormDivider({ thick }) {
|
||||
return (
|
||||
<hr
|
||||
className="my-2"
|
||||
style={{
|
||||
borderTopWidth: thick ? '3px' : '1px',
|
||||
marginLeft: '-48px',
|
||||
marginRight: '-48px',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
AppConfigFormDivider.propTypes = {
|
||||
thick: PropTypes.bool,
|
||||
};
|
||||
|
||||
AppConfigFormDivider.defaultProps = {
|
||||
thick: false,
|
||||
};
|
||||
@@ -4,6 +4,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Form, TransitionReplace } from '@edx/paragon';
|
||||
import FormSwitchGroup from '../../../../../generic/FormSwitchGroup';
|
||||
import messages from './messages';
|
||||
import AppConfigFormDivider from './AppConfigFormDivider';
|
||||
|
||||
function DivisionByGroupFields({
|
||||
onBlur,
|
||||
@@ -26,6 +27,7 @@ function DivisionByGroupFields({
|
||||
<TransitionReplace>
|
||||
{values.divideByCohorts ? (
|
||||
<React.Fragment key="open">
|
||||
<AppConfigFormDivider />
|
||||
<FormSwitchGroup
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
@@ -35,6 +37,7 @@ function DivisionByGroupFields({
|
||||
label={intl.formatMessage(messages.allowDivisionByUnitLabel)}
|
||||
helpText={intl.formatMessage(messages.allowDivisionByUnitHelp)}
|
||||
/>
|
||||
<AppConfigFormDivider />
|
||||
<FormSwitchGroup
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { TransitionReplace } from '@edx/paragon';
|
||||
import FormSwitchGroup from '../../../../../generic/FormSwitchGroup';
|
||||
import messages from './messages';
|
||||
import AppConfigFormDivider from './AppConfigFormDivider';
|
||||
|
||||
function InContextDiscussionFields({
|
||||
onBlur,
|
||||
@@ -22,6 +23,7 @@ function InContextDiscussionFields({
|
||||
label={intl.formatMessage(messages.inContextDiscussionLabel)}
|
||||
helpText={intl.formatMessage(messages.inContextDiscussionHelp)}
|
||||
/>
|
||||
<AppConfigFormDivider />
|
||||
<TransitionReplace>
|
||||
{values.inContextDiscussion ? (
|
||||
<React.Fragment key="open">
|
||||
@@ -34,6 +36,7 @@ function InContextDiscussionFields({
|
||||
label={intl.formatMessage(messages.gradedUnitPagesLabel)}
|
||||
helpText={intl.formatMessage(messages.gradedUnitPagesHelp)}
|
||||
/>
|
||||
<AppConfigFormDivider />
|
||||
<FormSwitchGroup
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
@@ -43,6 +46,7 @@ function InContextDiscussionFields({
|
||||
label={intl.formatMessage(messages.groupInContextSubsectionLabel)}
|
||||
helpText={intl.formatMessage(messages.groupInContextSubsectionHelp)}
|
||||
/>
|
||||
<AppConfigFormDivider />
|
||||
<FormSwitchGroup
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
@@ -52,6 +56,7 @@ function InContextDiscussionFields({
|
||||
label={intl.formatMessage(messages.allowUnitLevelVisibilityLabel)}
|
||||
helpText={intl.formatMessage(messages.allowUnitLevelVisibilityHelp)}
|
||||
/>
|
||||
<AppConfigFormDivider />
|
||||
</React.Fragment>
|
||||
) : <React.Fragment key="closed" />}
|
||||
|
||||
|
||||
@@ -29,10 +29,17 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Applied',
|
||||
description: 'Button label when the discussion configuration has been successfully submitted.',
|
||||
},
|
||||
selectDiscussionTool: {
|
||||
id: 'authoring.discussions.selectDiscussionTool',
|
||||
defaultMessage: 'Select discussion tool',
|
||||
description: 'A label for the first step of a wizard where the user chooses a discussion tool to configure.',
|
||||
|
||||
// App names
|
||||
'appName-piazza': {
|
||||
id: 'authoring.discussions.appConfigForm.appName-piazza',
|
||||
defaultMessage: 'Piazza',
|
||||
description: 'The name of the Piazza app.',
|
||||
},
|
||||
'appName-legacy': {
|
||||
id: 'authoring.discussions.appConfigForm.appName-legacy',
|
||||
defaultMessage: 'edX Discussions',
|
||||
description: 'The name of the Legacy edX Discussions app.',
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -32,23 +32,23 @@ const messages = defineMessages({
|
||||
|
||||
// Legacy
|
||||
'appName-legacy': {
|
||||
id: 'authoring.discussions.appName-legacy',
|
||||
defaultMessage: 'Legacy edX Discussions',
|
||||
id: 'authoring.discussions.appList.appName-legacy',
|
||||
defaultMessage: 'edX Discussions',
|
||||
description: 'The name of the Legacy edX Discussions app.',
|
||||
},
|
||||
'appDescription-legacy': {
|
||||
id: 'authoring.discussions.appDescription-legacy',
|
||||
id: 'authoring.discussions.appList.appDescription-legacy',
|
||||
defaultMessage: 'Start conversations with other learners, ask questions, and interact with other learners in the course.',
|
||||
description: 'A description of the Legacy edX Discussions app.',
|
||||
},
|
||||
// Piazza
|
||||
'appName-piazza': {
|
||||
id: 'authoring.discussions.appName-piazza',
|
||||
id: 'authoring.discussions.appList.appName-piazza',
|
||||
defaultMessage: 'Piazza',
|
||||
description: 'The name of the Piazza app.',
|
||||
},
|
||||
'appDescription-piazza': {
|
||||
id: 'authoring.discussions.appDescription-piazza',
|
||||
id: 'authoring.discussions.appList.appDescription-piazza',
|
||||
defaultMessage: 'Piazza is designed to connect students, TAs, and professors so every student can get the help they need when they need it.',
|
||||
description: 'A description of the Piazza app.',
|
||||
},
|
||||
|
||||
@@ -34,9 +34,9 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Applied',
|
||||
description: 'Button label when the discussion configuration has been successfully submitted.',
|
||||
},
|
||||
selectDiscussionTool: {
|
||||
id: 'authoring.discussions.selectDiscussionTool',
|
||||
defaultMessage: 'Select discussion tool',
|
||||
providerSelection: {
|
||||
id: 'authoring.discussions.providerSelection',
|
||||
defaultMessage: 'Provider selection',
|
||||
description: 'A label for the first step of a wizard where the user chooses a discussion tool to configure.',
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user