FEATURE: displays an error in preview for more than one event (#42)

FEATURE: displays an error in preview for more than one event (#42)

This commit also introduces client side tests, this plugin only had server side tests before.

diff --git a/assets/javascripts/initializers/discourse-post-event-decorator.js.es6 b/assets/javascripts/initializers/discourse-post-event-decorator.js.es6
index c654817..c3fbcbe 100644
--- a/assets/javascripts/initializers/discourse-post-event-decorator.js.es6
+++ b/assets/javascripts/initializers/discourse-post-event-decorator.js.es6
@@ -9,53 +9,66 @@ function _decorateEvent(api, cooked, post) {
   _attachWidget(api, cooked, post);
 }
 
-function _decorateEventPreview(api, cooked) {
-  const eventContainer = cooked.querySelector(".discourse-post-event");
-
-  if (eventContainer) {
-    if (!eventContainer.dataset.start) {
-      return;
-    }
+function _validEventPreview(eventContainer) {
+  eventContainer.innerHTML = "";
+  eventContainer.classList.add("discourse-post-event-preview");
+
+  const statusLocaleKey = `discourse_post_event.models.event.status.${eventContainer
+    .dataset.status || "public"}.title`;
+  if (I18n.lookup(statusLocaleKey, { locale: "en" })) {
+    const statusContainer = document.createElement("div");
+    statusContainer.classList.add("event-preview-status");
+    statusContainer.innerText = I18n.t(statusLocaleKey);
+    eventContainer.appendChild(statusContainer);
+  }
 
-    const eventPreviewContainer = document.createElement("div");
-    eventPreviewContainer.classList.add("discourse-post-event-preview");
+  const datesContainer = document.createElement("div");
+  datesContainer.classList.add("event-preview-dates");
 
-    const statusLocaleKey = `discourse_post_event.models.event.status.${eventContainer
-      .dataset.status || "public"}.title`;
-    if (I18n.lookup(statusLocaleKey, { locale: "en" })) {
-      const statusContainer = document.createElement("div");
-      statusContainer.classList.add("event-preview-status");
-      statusContainer.innerText = I18n.t(statusLocaleKey);
-      eventPreviewContainer.appendChild(statusContainer);
-    }
+  const startsAt = moment
+    .utc(eventContainer.dataset.start)
+    .tz(moment.tz.guess());
 
-    const datesContainer = document.createElement("div");
-    datesContainer.classList.add("event-preview-dates");
+  const endsAtValue = eventContainer.dataset.end;
+  const format = guessDateFormat(
+    startsAt,
+    endsAtValue && moment.utc(endsAtValue).tz(moment.tz.guess())
+  );
 
-    const startsAt = moment
-      .utc(eventContainer.dataset.start)
-      .tz(moment.tz.guess());
+  let datesString = `<span class='start'>${startsAt.format(format)}</span>`;
+  if (endsAtValue) {
+    datesString += ` → <span class='end'>${moment
+      .utc(endsAtValue)
+      .tz(moment.tz.guess())
+      .format(format)}</span>`;
+  }
+  datesContainer.innerHTML = datesString;
 
-    const endsAtValue = eventContainer.dataset.end;
-    const format = guessDateFormat(
-      startsAt,
-      endsAtValue && moment.utc(endsAtValue).tz(moment.tz.guess())
-    );
+  eventContainer.appendChild(datesContainer);
+}
 
-    let datesString = `<span class='start'>${startsAt.format(format)}</span>`;
-    if (endsAtValue) {
-      datesString += ` → <span class='end'>${moment
-        .utc(endsAtValue)
-        .tz(moment.tz.guess())
-        .format(format)}</span>`;
-    }
-    datesContainer.innerHTML = datesString;
+function _invalidEventPreview(eventContainer) {
+  eventContainer.classList.add(
+    "discourse-post-event-preview",
+    "alert",
+    "alert-error"
+  );
+  eventContainer.classList.remove("discourse-post-event");
+  eventContainer.innerText = I18n.t(
+    "discourse_post_event.preview.more_than_one_event"
+  );
+}
 
-    eventPreviewContainer.appendChild(datesContainer);
+function _decorateEventPreview(api, cooked) {
+  const eventContainers = cooked.querySelectorAll(".discourse-post-event");
 
-    eventContainer.innerHTML = "";
-    eventContainer.appendChild(eventPreviewContainer);
-  }
+  eventContainers.forEach((eventContainer, index) => {
+    if (index > 0) {
+      _invalidEventPreview(eventContainer);
+    } else {
+      _validEventPreview(eventContainer);
+    }
+  });
 }
 
 let _glued = [];
diff --git a/assets/stylesheets/common/discourse-post-event-preview.scss b/assets/stylesheets/common/discourse-post-event-preview.scss
index 8b73c01..2df21b7 100644
--- a/assets/stylesheets/common/discourse-post-event-preview.scss
+++ b/assets/stylesheets/common/discourse-post-event-preview.scss
@@ -1,11 +1,11 @@
 .discourse-post-event-preview {
   background: $secondary;
-  display: flex;
-  flex: 1 0 auto;
   align-items: center;
   flex-direction: column;
   padding: 0.5em;
   border: 1px solid $primary-low;
+  display: flex;
+  flex: 1 0 auto;
 
   .event-preview-status {
     margin: 0 0 0.5em 0;
@@ -14,4 +14,12 @@
   .event-preview-dates {
     font-weight: 700;
   }
+
+  &.alert-error {
+    border-color: lighten($danger, 25);
+  }
+}
+
+.discourse-post-event-preview + .discourse-post-event-preview {
+  margin-top: 1em;
 }
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 0af8c81..52d6d38 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -19,6 +19,8 @@ en:
       search: "Search..."
       group_availability: "%{group} availability"
     discourse_post_event:
+      preview:
+        more_than_one_event: You can’t have more than one event.
       edit_reason: Event updated
       models:
         invitee:
diff --git a/test/javascripts/acceptance/discourse-post-event/composer-preview-test.js.es6 b/test/javascripts/acceptance/discourse-post-event/composer-preview-test.js.es6
new file mode 100644
index 0000000..c241f75
--- /dev/null
+++ b/test/javascripts/acceptance/discourse-post-event/composer-preview-test.js.es6
@@ -0,0 +1,85 @@
+import discoursePostEventAcceptance, {
+  utcToLocal
+} from "./discourse-post-event-helper";
+
+discoursePostEventAcceptance("composer-preview");
+
+test("an event with a start date", async assert => {
+  await visit("/");
+  await click("#create-topic");
+  await fillIn(".d-editor-input", '[event start="2020-02-03"]\n[/event]');
+
+  const preview = find(".d-editor-preview .discourse-post-event");
+  assert.ok(exists(preview), "it creates the preview");
+  assert.equal(
+    preview.find(".event-preview-status").text(),
+    I18n.t("discourse_post_event.models.event.status.public.title"),
+    "it displays the status"
+  );
+  assert.equal(
+    preview.find(".event-preview-dates .start").text(),
+    utcToLocal("2020-02-03"),
+    "it displays the start date"
+  );
+});
+
+test("an event with a start date and end date", async assert => {
+  await visit("/");
+  await click("#create-topic");
+  await fillIn(
+    ".d-editor-input",
+    '[event start="2020-02-03" end="2002-03-04"]\n[/event]'
+  );
+
+  const preview = find(".d-editor-preview .discourse-post-event");
+  assert.equal(
+    preview.find(".event-preview-dates .start").text(),
+    utcToLocal("2020-02-03"),
+    "it displays the start date"
+  );
+  assert.equal(
+    preview.find(".event-preview-dates .end").text(),
+    utcToLocal("2002-03-04"),
+    "it displays the start date"
+  );
+});
+
+test("an event with a status", async assert => {
+  await visit("/");
+  await click("#create-topic");
+  await fillIn(
+    ".d-editor-input",
+    '[event status="private" start="2020-02-03"]\n[/event]'
+  );
+
+  const preview = find(".d-editor-preview .discourse-post-event");
+  assert.equal(
+    preview.find(".event-preview-status").text(),
+    I18n.t("discourse_post_event.models.event.status.private.title"),
+    "it displays the status"
+  );
+});
+
+test("more than one event", async assert => {
+  await visit("/");
+  await click("#create-topic");
+  await fillIn(
+    ".d-editor-input",
+    '[event start="2020-02-03"]\n[/event]\n\n[event start="2021-04-03"]\n[/event]'
+  );
+
+  const preview = find(
+    ".d-editor-preview .discourse-post-event-preview[data-start=2020-02-03]"
+  );
+  assert.ok(exists(preview), "it displays the first event");
+
+  const errorPreview = find(

[... diff too long, it was truncated ...]

GitHub sha: dca3145d

This commit appears in #42 which was merged by jjaffeux.