diff --git a/lang/en-US.json b/lang/en-US.json index 5e8c971c6..f66f78449 100644 --- a/lang/en-US.json +++ b/lang/en-US.json @@ -150,7 +150,7 @@ "Admin.EditChallenge.form.includeCheckinHashtag.value.true.label": "Automatically append `#maproulette` hashtag (highly recommended)", "Admin.EditChallenge.form.instruction.description": "The instruction tells a mapper how to resolve a Task in your Challenge. This is what mappers see in the Instructions box every time a task is loaded, and is the primary piece of information for the mapper about how to solve the task, so think about this field carefully. You can include links to the OSM wiki or any other hyperlink if you want, because this field supports [Markdown](https://learn.maproulette.org/documentation/markdown/). You can also reference feature properties from your GeoJSON with simple [mustache tags](https://learn.maproulette.org/documentation/mustache-tag-replacement/): e.g. `'{{address}}'` would be replaced with the value of the `address` property, allowing for basic customization of instructions for each task. This field is required.", "Admin.EditChallenge.form.instruction.label": "Detailed Instructions for Mappers", - "Admin.EditChallenge.form.instructionsDescription": "Instructions must be longer than {minLength} characters.", + "Admin.EditChallenge.form.instructionsDescription": "We recommend instructions be longer than {minLength} characters.", "Admin.EditChallenge.form.limitReviewTags.description": "Allow other tags during task review?", "Admin.EditChallenge.form.limitTags.description": "Allow other tags during task completion?", "Admin.EditChallenge.form.localGeoJson.description": "Please upload the local GeoJSON file from your computer", @@ -924,14 +924,14 @@ "Form.textUpload.promptGeoJSON": "Drop GeoJSON file here or click to select file", "Form.textUpload.promptJSON": "Drop JSON file here or click to select file", "Form.textUpload.readonly": "Existing file will be used", + "GeoJSONUploadModal.countingTasks": "Counting tasks…", "GeoJSONUploadModal.dropzone.label": "Drop a GeoJSON file here or click to upload", "GeoJSONUploadModal.error.invalid": "Invalid GeoJSON: {error}", "GeoJSONUploadModal.error.noPolygons": "No Polygon features found in file", + "GeoJSONUploadModal.error.tooManyTasks": "Too many tasks ({count}). Maximum allowed is {max}.", "GeoJSONUploadModal.header": "Create Virtual Challenge from GeoJSON", "GeoJSONUploadModal.success.polygonsLoaded": "{count, plural, one {# polygon} other {# polygons}} loaded", "GeoJSONUploadModal.taskCount": "{count, plural, one {# task} other {# tasks}} found in area", - "GeoJSONUploadModal.countingTasks": "Counting tasks\u2026", - "GeoJSONUploadModal.error.tooManyTasks": "Too many tasks ({count}). Maximum allowed is {max}.", "GlobalActivity.title": "Global Activity", "Grant.Role.admin": "Admin", "Grant.Role.read": "Read", @@ -1510,6 +1510,7 @@ "WidgetWorkspace.controls.editConfiguration.label": "Edit Layout", "WidgetWorkspace.controls.exportConfiguration.label": "Export Layout", "WidgetWorkspace.controls.importConfiguration.label": "Import Layout", + "WidgetWorkspace.controls.layoutName.label": "Layout Name", "WidgetWorkspace.controls.resetConfiguration.label": "Reset Layout to Default", "WidgetWorkspace.controls.saveAsDefault.label": "Save as My Default", "WidgetWorkspace.controls.saveConfiguration.label": "Done Editing", diff --git a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenge.jsx b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenge.jsx index 89ae556d4..72bac4f28 100644 --- a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenge.jsx +++ b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/EditChallenge.jsx @@ -231,6 +231,7 @@ export class EditChallenge extends Component { .finally(() => { this.validationPromise = null; }); + return errors; }; diff --git a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Messages.js b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Messages.js index 344498f19..b4e704f18 100644 --- a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Messages.js +++ b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Messages.js @@ -831,7 +831,7 @@ will not be able to make sense of it. instructionsDescription: { id: "Admin.EditChallenge.form.instructionsDescription", - defaultMessage: "Instructions must be longer than {minLength} characters.", + defaultMessage: "We recommend instructions be longer than {minLength} characters.", }, nameDescription: { diff --git a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Schemas/InstructionsSchema.js b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Schemas/InstructionsSchema.js index ccd34dc01..e22e8bdef 100644 --- a/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Schemas/InstructionsSchema.js +++ b/src/components/AdminPane/Manage/ManageChallenges/EditChallenge/Schemas/InstructionsSchema.js @@ -31,9 +31,6 @@ const validateMinLength = (val) => { }; export const jsSchema = (intl) => { - const minLengthEnvValue = window.env.REACT_APP_CHALLENGE_INSTRUCTIONS_MIN_LENGTH; - const instructionsMinLength = validateMinLength(minLengthEnvValue); - const schemaFields = { $schema: "http://json-schema.org/draft-07/schema#", type: "object", @@ -41,10 +38,6 @@ export const jsSchema = (intl) => { instruction: { title: intl.formatMessage(messages.instructionLabel), type: "string", - minLength: instructionsMinLength, - description: intl.formatMessage(messages.instructionsDescription, { - minLength: `${instructionsMinLength}`, - }), }, difficulty: { title: intl.formatMessage(messages.difficultyLabel), @@ -75,12 +68,19 @@ export const jsSchema = (intl) => { * > proper markup */ export const uiSchema = (intl, user, challengeData, extraErrors, options = {}) => { + const minLengthEnvValue = 150; + const instructionsMinLength = validateMinLength(minLengthEnvValue); + const uiSchemaFields = { "ui:order": ["instruction", "difficulty"], instruction: { "ui:field": "markdown", "ui:help": intl.formatMessage(messages.instructionDescription), "ui:previewNote": intl.formatMessage(messages.addMustachePreviewNote), + "ui:recommendedMinLength": instructionsMinLength, + "ui:recommendedMinLengthMessage": intl.formatMessage(messages.instructionsDescription, { + minLength: `${instructionsMinLength}`, + }), "ui:groupHeader": options.longForm ? intl.formatMessage(messages.instructionsStepHeader) : undefined, diff --git a/src/components/Custom/RJSFFormFieldAdapter/RJSFFormFieldAdapter.jsx b/src/components/Custom/RJSFFormFieldAdapter/RJSFFormFieldAdapter.jsx index 53c5443df..3799a9bc4 100644 --- a/src/components/Custom/RJSFFormFieldAdapter/RJSFFormFieldAdapter.jsx +++ b/src/components/Custom/RJSFFormFieldAdapter/RJSFFormFieldAdapter.jsx @@ -294,9 +294,19 @@ export const MarkdownEditField = (props) => { const [showingPreview, setShowingPreview] = useState(false); const [formValues, setFormValues] = useState({}); + const recommendedMinLength = props.uiSchema["ui:recommendedMinLength"]; + const recommendedMinLengthMessage = props.uiSchema["ui:recommendedMinLengthMessage"]; + const isBelowRecommended = + recommendedMinLength > 0 && (props.formData || "").length < recommendedMinLength; + return ( + {isBelowRecommended && recommendedMinLengthMessage && ( +
+ {recommendedMinLengthMessage} (current character count: {(props.formData || "").length}) +
+ )}