Skip to content

Commit d94b5d7

Browse files
committed
Improving the markdown edit and the exercise texts edit form.
1 parent 293b4b3 commit d94b5d7

File tree

7 files changed

+71
-28
lines changed

7 files changed

+71
-28
lines changed

src/components/forms/EditAssignmentLocalizedTextsForm/EditAssignmentLocalizedTextsForm.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { reduxForm, FieldArray } from 'redux-form';
44
import { FormattedMessage } from 'react-intl';
55
import { lruMemoize } from 'reselect';
66

7-
import { WarningIcon } from '../../icons';
7+
import { RefreshIcon, WarningIcon } from '../../icons';
88
import LocalizedTextsFormField from '../LocalizedTextsFormField';
99
import SubmitButton from '../SubmitButton';
10-
import Explanation from '../../widgets/Explanation';
1110
import Callout from '../../widgets/Callout';
11+
import Button, { TheButtonGroup } from '../../widgets/TheButton';
1212
import {
1313
getLocalizedTextsInitialValues,
1414
validateLocalizedTextsFormData,
@@ -64,6 +64,7 @@ class EditAssignmentLocalizedTextsForm extends Component {
6464
dirty,
6565
submitting,
6666
handleSubmit,
67+
reset,
6768
submitFailed,
6869
submitSucceeded,
6970
asyncValidating = false,
@@ -93,17 +94,25 @@ class EditAssignmentLocalizedTextsForm extends Component {
9394
{warning && !error && <Callout variant="warning">{warning}</Callout>}
9495

9596
<div className="text-center">
96-
<SubmitButton
97-
id="editAssignmentLocalizedTextsForm"
98-
invalid={invalid}
99-
submitting={submitting}
100-
dirty={dirty}
101-
hasSucceeded={submitSucceeded}
102-
hasFailed={submitFailed}
103-
handleSubmit={handleSubmit(this.onSubmitWrapper)}
104-
asyncValidating={asyncValidating}
105-
messages={submitButtonMessages}
106-
/>
97+
<TheButtonGroup>
98+
{dirty && (
99+
<Button type="reset" onClick={reset} variant="danger">
100+
<RefreshIcon gapRight={2} />
101+
<FormattedMessage id="generic.reset" defaultMessage="Reset" />
102+
</Button>
103+
)}
104+
<SubmitButton
105+
id="editAssignmentLocalizedTextsForm"
106+
invalid={invalid}
107+
submitting={submitting}
108+
dirty={dirty}
109+
hasSucceeded={submitSucceeded}
110+
hasFailed={submitFailed}
111+
handleSubmit={handleSubmit(this.onSubmitWrapper)}
112+
asyncValidating={asyncValidating}
113+
messages={submitButtonMessages}
114+
/>
115+
</TheButtonGroup>
107116

108117
{submitFailed && (
109118
<span className="ms-4 text-danger">

src/components/forms/Fields/MarkdownTextAreaField.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { Component } from 'react';
22
import PropTypes from 'prop-types';
33
import { FormattedMessage } from 'react-intl';
44
import { Form, FormLabel, Row, Col, Modal } from 'react-bootstrap';
5+
import classnames from 'classnames';
56

67
import TextAreaField from './TextAreaField.js';
78
import Markdown from '../../widgets/Markdown';
@@ -42,7 +43,7 @@ class MarkdownTextAreaField extends Component {
4243

4344
render() {
4445
const {
45-
meta: { error, warning },
46+
meta: { dirty, error, warning },
4647
label = null,
4748
input: { name, value },
4849
disabled,
@@ -60,8 +61,15 @@ class MarkdownTextAreaField extends Component {
6061
<FormLabel className={error ? 'text-danger' : warning ? 'text-warning' : undefined}>{label}</FormLabel>
6162
)}
6263
<div
63-
className={`${styles.preview} ${value.length === 0 ? styles.previewEmpty : ''} ${styles.clickable} mb-3`}
64-
onDoubleClick={this.showDialog}>
64+
className={classnames({
65+
[styles.preview]: true,
66+
[styles.previewEmpty]: value.length === 0,
67+
[styles.clickable]: !disabled,
68+
[styles.disabled]: disabled,
69+
[styles.dirty]: dirty,
70+
'mb-3': true,
71+
})}
72+
onDoubleClick={disabled ? null : this.showDialog}>
6573
<Icon icon="pencil" className={styles.editIcon} />
6674
{value.length === 0 && (
6775
<>
@@ -180,6 +188,7 @@ class MarkdownTextAreaField extends Component {
180188

181189
MarkdownTextAreaField.propTypes = {
182190
meta: PropTypes.shape({
191+
dirty: PropTypes.bool,
183192
error: PropTypes.any,
184193
warning: PropTypes.any,
185194
}),

src/components/forms/Fields/MarkdownTextAreaField.less

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,18 @@
1313
padding-bottom: 0.75rem;
1414
}
1515

16-
.clickable {
16+
.clickable:not(.disabled) {
1717
cursor: pointer;
1818
position: relative;
1919
}
2020

21-
.clickable:hover {
21+
.clickable:not(.disabled):hover {
2222
background: #eeeee0;
2323
border-color: #eec;
2424
box-shadow: 0 0 3px #ffa;
2525
}
2626

27-
.clickable .editIcon {
27+
.clickable:not(.disabled) .editIcon {
2828
position: absolute;
2929
text-rendering: auto;
3030
top: 0.5rem;
@@ -33,9 +33,12 @@
3333
height: 1.5rem;
3434
opacity: 0;
3535
}
36-
.clickable:hover .editIcon{
36+
.clickable:not(.disabled):hover .editIcon{
3737
opacity: 0.3;
3838
}
39+
.clickable.disabled .editIcon {
40+
display:none;
41+
}
3942

4043
.editorToggle {
4144
margin-top: 0.20rem;
@@ -50,3 +53,13 @@
5053
resize: none;
5154
height: calc(90vh - 6rem);
5255
}
56+
57+
.preview.disabled {
58+
background: #ccc;
59+
opacity: 0.75;
60+
}
61+
62+
.preview.dirty {
63+
background: #f4f4ff;
64+
border-color: #cce;
65+
}

src/components/forms/LocalizedTextsFormField/SharedExerciseAssignmentLocalizedFields.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,18 @@ const SharedExerciseAssignmentLocalizedFields = ({ prefix, enabled, previewPrepr
4747
maxLength={1024}
4848
disabled={!enabled}
4949
label={
50-
<FormattedMessage
51-
id="app.editAssignmentForm.localized.link"
52-
defaultMessage="Link to an external complete description:"
53-
/>
50+
<>
51+
<FormattedMessage
52+
id="app.editAssignmentForm.localized.link"
53+
defaultMessage="Link to an external complete description:"
54+
/>
55+
<Explanation id="exercise-link-explanation">
56+
<FormattedMessage
57+
id="app.editAssignmentForm.localized.linkExplanation"
58+
defaultMessage="An URL pointing to an external Markdown document containing the complete exercise description. If the document is accessible in the cross-site mode, ReCodEx will use it to download and render the description seamlessly. If you wish to emphasize, this is a link and visualize it as such, consider using a Markdown link in the 'Complete description' field above."
59+
/>
60+
</Explanation>
61+
</>
5462
}
5563
validate={isURL}
5664
/>

src/locales/cs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@
327327
"app.editAssignmentForm.localized.description": "Krátký popis pro vedoucí:",
328328
"app.editAssignmentForm.localized.descriptionExplanation": "Tento krátký popis je viditelný pouze pro vedoucí a měl by stručně shrnovat úlohu. V textu můžete použít klíčové identifikátory odkazů na soubory úlohy (%%link-key%%), které budou po vykreslení Markdownu nahrazeny správnými URL pro stažení souborů.",
329329
"app.editAssignmentForm.localized.link": "Odkaz na externí (kompletní) zadání:",
330+
"app.editAssignmentForm.localized.linkExplanation": "URL odkazující na externí Markdown dokument obsahující kompletní popis úlohy. Pokud je dokument stažitelný z cizích stránek (cross-origin), ReCodEx jej stáhne a vykreslí namísto interního textu. Pokud naopak chcete zdůraznit, že se jedná o externí odkaz a vizualizovat jej jako takový, zvažte použití Markdown odkazu v poli 'Kompletní zadání' výše.",
330331
"app.editAssignmentForm.localized.studentHint": "Dodatečná nápověda pro studenty:",
331332
"app.editAssignmentForm.localized.studentHintExplanation": "Tato nápověda je svázána výhradně se zadanou úlohou a není tedy ovlivněna synchronizací s původní úlohou. Nicméně v tomto formuláři jsou dostupné pouze lokalizace, které jsou povoleny v zadání úlohy.",
332333
"app.editAssignmentForm.localized.urlValidation": "Daný řetězec neobsahuje platné URL.",

src/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@
327327
"app.editAssignmentForm.localized.description": "Short description for supervisors:",
328328
"app.editAssignmentForm.localized.descriptionExplanation": "This descriptions is visible only to supervisors and should briefly summarize the exercise. You can use exercise file link keys in the text (%%link-key%%) which will be replaced with proper URLs for file downloads in after Markdown rendering.",
329329
"app.editAssignmentForm.localized.link": "Link to an external complete description:",
330+
"app.editAssignmentForm.localized.linkExplanation": "An URL pointing to an external Markdown document containing the complete exercise description. If the document is accessible in the cross-site mode, ReCodEx will use it to download and render the description seamlessly. If you wish to emphasize, this is a link and visualize it as such, consider using a Markdown link in the 'Complete description' field above.",
330331
"app.editAssignmentForm.localized.studentHint": "Additional hints for students:",
331332
"app.editAssignmentForm.localized.studentHintExplanation": "These hints are assignment-specific and they do not synchronize with the exercise texts. However, only localizations that are enabled in the exercise specification are available for hints.",
332333
"app.editAssignmentForm.localized.urlValidation": "Given string is not a valid URL.",

src/pages/ExerciseAssignments/ExerciseAssignments.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class ExerciseAssignments extends Component {
8080
}
8181

8282
multiAssignFormInitialValues = lruMemoize(
83-
(visibleGroups, runtimeEnvironments, solutionFilesLimit, solutionSizeLimit) => {
83+
(visibleGroups, runtimeEnvironments, solutionFilesLimit, solutionSizeLimit, localizedTexts) => {
8484
const groups = visibleGroups.reduce((acc, { id }) => {
8585
acc[`id${id}`] = false;
8686
return acc;
@@ -97,6 +97,7 @@ class ExerciseAssignments extends Component {
9797
groups,
9898
runtimeEnvironmentIds: Object.keys(enabledRuntime),
9999
enabledRuntime,
100+
localizedTexts,
100101
});
101102
}
102103
);
@@ -106,8 +107,8 @@ class ExerciseAssignments extends Component {
106107

107108
return Promise.all(
108109
formData.groups.map(groupId => {
109-
return assignExercise(groupId).then(({ value: { id, localizedTexts, version } }) =>
110-
editAssignment(id, Object.assign({}, { localizedTexts, version }, formData))
110+
return assignExercise(groupId).then(({ value: { id, version } }) =>
111+
editAssignment(id, Object.assign({}, { version }, formData))
111112
);
112113
})
113114
);
@@ -220,7 +221,8 @@ class ExerciseAssignments extends Component {
220221
assignableGroups,
221222
exercise.runtimeEnvironments,
222223
exercise.solutionFilesLimit,
223-
exercise.solutionSizeLimit
224+
exercise.solutionSizeLimit,
225+
exercise.localizedTexts
224226
)}
225227
onSubmit={this.assignExercise}
226228
groups={assignableGroups}

0 commit comments

Comments
 (0)