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:
David Joy
2021-04-07 16:29:00 -04:00
committed by GitHub
parent b51ac0235c
commit 3dcb5a2889
11 changed files with 145 additions and 98 deletions

View File

@@ -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' }} />
),

View File

@@ -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>
);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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}

View File

@@ -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,
};

View File

@@ -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}

View File

@@ -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" />}

View File

@@ -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.',
},
});

View File

@@ -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.',
},

View File

@@ -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.',
},
});