From 656d698da14cf90b11845bb579075a0620fa4ad4 Mon Sep 17 00:00:00 2001 From: Anna Buianova Date: Thu, 21 Jul 2022 13:53:54 +0300 Subject: [PATCH] Modify datetime picker for scheduling articles (#18153) * Accept separate date and time for published_at * Separate date and time inputs for published_at in post options (scheduling) * Removed the commented line * Removed unused htmlFor publishedAt Co-authored-by: Suzanne Aitchison * Added aria-label for published at date Co-authored-by: Suzanne Aitchison * Added aria-label for published at time Co-authored-by: Suzanne Aitchison * Added htmlFor for schedule label Co-authored-by: Suzanne Aitchison --- app/controllers/articles_controller.rb | 9 +++- app/javascript/article-form/articleForm.jsx | 23 ++++++-- .../article-form/components/EditorActions.jsx | 15 ++++-- .../article-form/components/Options.jsx | 54 +++++++++---------- .../requests/articles/articles_create_spec.rb | 27 ++++++++-- .../requests/articles/articles_update_spec.rb | 7 ++- 6 files changed, 90 insertions(+), 45 deletions(-) diff --git a/app/controllers/articles_controller.rb b/app/controllers/articles_controller.rb index ef43c4444..eb1f3c4db 100644 --- a/app/controllers/articles_controller.rb +++ b/app/controllers/articles_controller.rb @@ -306,6 +306,7 @@ class ArticlesController < ApplicationController %i[ title body_markdown main_image published description video_thumbnail_url tag_list canonical_url series collection_id archived published_at timezone + published_at_date published_at_time ] end @@ -319,10 +320,14 @@ class ArticlesController < ApplicationController end time_zone_str = params["article"].delete("timezone") - if params["article"]["published_at"] + + time = params["article"].delete("published_at_time") + date = params["article"].delete("published_at_date") + + if date.present? time_zone = Time.find_zone(time_zone_str) time_zone ||= Time.find_zone("UTC") - params["article"]["published_at"] = time_zone.parse(params["article"]["published_at"]) + params["article"]["published_at"] = time_zone.parse("#{date} #{time}") end @article_params_json = params.require(:article).permit(allowed_params) diff --git a/app/javascript/article-form/articleForm.jsx b/app/javascript/article-form/articleForm.jsx index 9838e3793..bd96913b8 100644 --- a/app/javascript/article-form/articleForm.jsx +++ b/app/javascript/article-form/articleForm.jsx @@ -2,6 +2,7 @@ import { h, Component } from 'preact'; import PropTypes from 'prop-types'; import linkState from 'linkstate'; import postscribe from 'postscribe'; +import moment from 'moment'; import { KeyboardShortcuts } from '../shared/components/useKeyboardShortcuts'; import { embedGists } from '../utilities/gist'; import { submitArticle, previewArticle } from './actions'; @@ -18,7 +19,7 @@ import { getOSKeyboardModifierKeyString } from '@utilities/runtime'; /* global activateRunkitTags */ /* - Although the state fields: id, description, canonicalUrl, publishedAt, series, allSeries and + Although the state fields: id, description, canonicalUrl, publishedAtDate, publishedAtTime, series, allSeries and editing are not used in this file, they are important to the editor. */ @@ -96,6 +97,14 @@ export class ArticleForm extends Component { } : {}; + this.publishedAtTime, (this.publishedAtDate = ''); + + if (this.article.published_at) { + const publishedAt = moment(this.article.published_at); + this.publishedAtTime = publishedAt.format('HH:mm'); + this.publishedAtDate = publishedAt.format('YYYY-MM-DD'); + } + this.state = { formKey: new Date().toISOString(), id: this.article.id || null, // eslint-disable-line react/no-unused-state @@ -103,7 +112,8 @@ export class ArticleForm extends Component { tagList: this.article.cached_tag_list || '', description: '', // eslint-disable-line react/no-unused-state canonicalUrl: this.article.canonical_url || '', // eslint-disable-line react/no-unused-state - publishedAt: this.article.published_at || '', // eslint-disable-line react/no-unused-state + publishedAtTime: this.publishedAtTime, + publishedAtDate: this.publishedAtDate, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || '', // eslint-disable-line react/no-unused-state series: this.article.series || '', // eslint-disable-line react/no-unused-state allSeries: this.article.all_series || [], // eslint-disable-line react/no-unused-state @@ -335,7 +345,8 @@ export class ArticleForm extends Component { tagList: this.article.cached_tag_list || '', description: '', // eslint-disable-line react/no-unused-state canonicalUrl: this.article.canonical_url || '', // eslint-disable-line react/no-unused-state - publishedAt: this.article.published_at || '', // eslint-disable-line react/no-unused-state + publishedAtTime: this.publishedAtTime, + publishedAtDate: this.publishedAtDate, series: this.article.series || '', // eslint-disable-line react/no-unused-state allSeries: this.article.all_series || [], // eslint-disable-line react/no-unused-state bodyMarkdown: this.article.body_markdown || '', @@ -397,7 +408,8 @@ export class ArticleForm extends Component { tagList, bodyMarkdown, published, - publishedAt, + publishedAtTime, + publishedAtDate, previewShowing, previewLoading, previewResponse, @@ -498,7 +510,8 @@ export class ArticleForm extends Component { now; + const now = moment(); + const publishedAtObj = publishedAtDate + ? moment(`${publishedAtDate} ${publishedAtTime || '00:00'}`) + : now; + const schedule = publishedAtObj > now; const saveButtonText = schedule ? 'Schedule' @@ -95,7 +99,8 @@ EditorActions.propTypes = { onSaveDraft: PropTypes.func.isRequired, onPublish: PropTypes.func.isRequired, published: PropTypes.bool.isRequired, - publishedAt: PropTypes.string.isRequired, + publishedAtTime: PropTypes.string.isRequired, + publishedAtDate: PropTypes.string.isRequired, schedulingEnabled: PropTypes.bool.isRequired, edited: PropTypes.bool.isRequired, version: PropTypes.string.isRequired, diff --git a/app/javascript/article-form/components/Options.jsx b/app/javascript/article-form/components/Options.jsx index 58ac3b19a..c36c570d0 100644 --- a/app/javascript/article-form/components/Options.jsx +++ b/app/javascript/article-form/components/Options.jsx @@ -1,5 +1,6 @@ import { h } from 'preact'; import PropTypes from 'prop-types'; +import moment from 'moment'; import { Dropdown, ButtonNew as Button } from '@crayons'; import CogIcon from '@images/cog.svg'; @@ -12,24 +13,11 @@ import CogIcon from '@images/cog.svg'; * @param {Function} props.onConfigChange Callback for when the config options have changed */ -function toISOStringLocal(datetime) { - const month = (datetime.getMonth() + 1).toString().padStart(2, '0'); - const day = datetime.getDate().toString().padStart(2, '0'); - return [ - datetime.getFullYear(), - '-', - month, - '-', - day, - 'T', - datetime.toLocaleTimeString(), - ].join(''); -} - export const Options = ({ passedData: { published = false, - publishedAt = '', + publishedAtDate = '', + publishedAtTime = '', timezone = Intl.DateTimeFormat().resolvedOptions().timeZone, allSeries = [], canonicalUrl = '', @@ -43,13 +31,9 @@ export const Options = ({ let publishedField = ''; let existingSeries = ''; let publishedAtField = ''; - const publishedDate = new Date(publishedAt); - const currentDate = new Date(); - const localPublishedAt = toISOStringLocal(publishedDate); - const minPublishedAt = toISOStringLocal(currentDate); - - const readonlyPublishedAt = published && publishedDate < currentDate; + const readonlyPublishedAt = + published && moment(`${publishedAtDate} ${publishedAtTime}`) < moment(); if (allSeries.length > 0) { const seriesNames = allSeries.map((name, index) => { @@ -92,20 +76,33 @@ export const Options = ({ ); } + if (schedulingEnabled && !readonlyPublishedAt) { + const currentDate = moment().format('YYYY-MM-DD'); publishedAtField = (
-