DEV: [gjs-codemod] Convert automation/styleguide/other to gjs

Co-authored-by: Jarek Radosz <jarek@cvx.dev>
This commit is contained in:
David Taylor
2025-04-14 15:36:16 +01:00
parent 3462113bd4
commit 7b2b08cf89
144 changed files with 4859 additions and 3715 deletions

View File

@ -794,3 +794,7 @@ export function isPrimaryTab() {
} }
}); });
} }
export function optionalRequire(path, name = "default") {
return require.has(path) && require(path)[name];
}

View File

@ -1,31 +1,36 @@
import Component from "@ember/component"; import Component, { Input } from "@ember/component";
import { hash } from "@ember/helper";
import { tagName } from "@ember-decorators/component"; import { tagName } from "@ember-decorators/component";
import PluginOutlet from "discourse/components/plugin-outlet";
import icon from "discourse/helpers/d-icon";
@tagName("") @tagName("")
export default class Checkbox extends Component {} export default class Checkbox extends Component {
<template>
<label class="wizard-container__label">
<PluginOutlet
@name="wizard-checkbox"
@outletArgs={{hash disabled=this.field.disabled}}
>
<Input
@type="checkbox"
disabled={{this.field.disabled}}
class="wizard-container__checkbox"
@checked={{this.field.value}}
/>
<span class="wizard-container__checkbox-slider"></span>
{{#if this.field.icon}}
{{icon this.field.icon}}
{{/if}}
<span class="wizard-container__checkbox-label">
{{this.field.placeholder}}
</span>
</PluginOutlet>
<label class="wizard-container__label"> <PluginOutlet
<PluginOutlet @name="below-wizard-checkbox"
@name="wizard-checkbox" @outletArgs={{hash disabled=this.field.disabled}}
@outletArgs={{hash disabled=this.field.disabled}} />
> </label>
<Input </template>
@type="checkbox" }
disabled={{this.field.disabled}}
class="wizard-container__checkbox"
@checked={{this.field.value}}
/>
<span class="wizard-container__checkbox-slider"></span>
{{#if this.field.icon}}
{{d-icon this.field.icon}}
{{/if}}
<span class="wizard-container__checkbox-label">
{{this.field.placeholder}}
</span>
</PluginOutlet>
<PluginOutlet
@name="below-wizard-checkbox"
@outletArgs={{hash disabled=this.field.disabled}}
/>
</label>

View File

@ -1,5 +1,7 @@
import Component from "@ember/component"; import Component, { Input } from "@ember/component";
import { on } from "@ember/modifier";
import { action, set } from "@ember/object"; import { action, set } from "@ember/object";
import icon from "discourse/helpers/d-icon";
export default class Checkboxes extends Component { export default class Checkboxes extends Component {
init(...args) { init(...args) {
@ -30,21 +32,23 @@ export default class Checkboxes extends Component {
} }
this.set("field.value", newFieldValue); this.set("field.value", newFieldValue);
} }
}
{{#each this.field.choices as |c|}} <template>
<div class="checkbox-field-choice {{this.fieldClass}}"> {{#each this.field.choices as |c|}}
<label id={{c.id}} value={{c.label}}> <div class="checkbox-field-choice {{this.fieldClass}}">
<Input <label id={{c.id}} value={{c.label}}>
@type="checkbox" <Input
class="wizard-container__checkbox" @type="checkbox"
@checked={{c.checked}} class="wizard-container__checkbox"
{{on "click" (action "changed")}} @checked={{c.checked}}
/> {{on "click" (action "changed")}}
{{#if c.icon}} />
{{d-icon c.icon}} {{#if c.icon}}
{{/if}} {{icon c.icon}}
{{c.label}} {{/if}}
</label> {{c.label}}
</div> </label>
{{/each}} </div>
{{/each}}
</template>
}

View File

@ -2,6 +2,8 @@ import Component from "@ember/component";
import { classNameBindings } from "@ember-decorators/component"; import { classNameBindings } from "@ember-decorators/component";
@classNameBindings(":wizard-image-preview", "fieldClass") @classNameBindings(":wizard-image-preview", "fieldClass")
export default class Generic extends Component {} export default class Generic extends Component {
<template>
<img src={{this.field.value}} class={{this.fieldClass}} /> <img src={{this.field.value}} class={{this.fieldClass}} />
</template>
}

View File

@ -446,6 +446,17 @@ export default class PreviewBase extends Component {
unreadTextX + ctx.measureText(unreadText).width + headerMargin * 2.0; unreadTextX + ctx.measureText(unreadText).width + headerMargin * 2.0;
ctx.fillText(topText, topTextX, pillButtonTextY); ctx.fillText(topText, topTextX, pillButtonTextY);
} }
<template>
<div class="wizard-container__preview">
<canvas
width={{this.elementWidth}}
height={{this.elementHeight}}
style={{this.canvasStyle}}
>
</canvas>
</div>
</template>
} }
function loadImage(src) { function loadImage(src) {
@ -457,12 +468,3 @@ function loadImage(src) {
img.src = getUrl(src); img.src = getUrl(src);
return new Promise((resolve) => (img.onload = () => resolve(img))); return new Promise((resolve) => (img.onload = () => resolve(img)));
} }
<div class="wizard-container__preview">
<canvas
width={{this.elementWidth}}
height={{this.elementHeight}}
style={{this.canvasStyle}}
>
</canvas>
</div>

View File

@ -1,3 +1,4 @@
import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { observes } from "@ember-decorators/object"; import { observes } from "@ember-decorators/object";
import { bind } from "discourse/lib/decorators"; import { bind } from "discourse/lib/decorators";
@ -247,35 +248,37 @@ export default class Index extends PreviewBaseComponent {
event?.preventDefault(); event?.preventDefault();
this.set("previewTopic", true); this.set("previewTopic", true);
} }
<template>
<div class="previews {{if this.draggingActive 'dragging'}}">
<div class="wizard-container__preview topic-preview">
<canvas
width={{this.elementWidth}}
height={{this.elementHeight}}
style={{this.canvasStyle}}
>
</canvas>
</div>
<div class="wizard-container__preview homepage-preview">
<this.HomepagePreview @wizard={{this.wizard}} @step={{this.step}} />
</div>
</div>
<div class="preview-nav">
<a
href
class="preview-nav-button {{if this.previewTopic 'active'}}"
{{on "click" this.setPreviewTopic}}
>
{{i18n "wizard.previews.topic_preview"}}
</a>
<a
href
class="preview-nav-button {{unless this.previewTopic 'active'}}"
{{on "click" this.setPreviewHomepage}}
>
{{i18n "wizard.previews.homepage_preview"}}
</a>
</div>
</template>
} }
<div class="previews {{if this.draggingActive 'dragging'}}">
<div class="wizard-container__preview topic-preview">
<canvas
width={{this.elementWidth}}
height={{this.elementHeight}}
style={{this.canvasStyle}}
>
</canvas>
</div>
<div class="wizard-container__preview homepage-preview">
<this.HomepagePreview @wizard={{this.wizard}} @step={{this.step}} />
</div>
</div>
<div class="preview-nav">
<a
href
class="preview-nav-button {{if this.previewTopic 'active'}}"
{{on "click" this.setPreviewTopic}}
>
{{i18n "wizard.previews.topic_preview"}}
</a>
<a
href
class="preview-nav-button {{unless this.previewTopic 'active'}}"
{{on "click" this.setPreviewHomepage}}
>
{{i18n "wizard.previews.homepage_preview"}}
</a>
</div>

View File

@ -1,11 +1,13 @@
import Component from "@ember/component"; import Component, { Input } from "@ember/component";
export default class Text extends Component {} export default class Text extends Component {
<template>
<Input <Input
id={{this.field.id}} id={{this.field.id}}
@value={{this.field.value}} @value={{this.field.value}}
class="wizard-container__text-input" class="wizard-container__text-input"
placeholder={{this.field.placeholder}} placeholder={{this.field.placeholder}}
tabindex="9" tabindex="9"
/> />
</template>
}

View File

@ -29,14 +29,16 @@ export default class ColorPalettesRow extends SelectKitRowComponent {
return ""; return "";
} }
} }
<template>
<span class="name">
{{this.label}}
</span>
{{#if this.item.colors}}
<div class="palettes" style={{this.backgroundColor}}>
{{this.palettes}}
</div>
{{/if}}
</template>
} }
<span class="name">
{{this.label}}
</span>
{{#if this.item.colors}}
<div class="palettes" style={{this.backgroundColor}}>
{{this.palettes}}
</div>
{{/if}}

View File

@ -15,6 +15,8 @@ export default class CreateColorRow extends SelectKitRowComponent {
: `#${color}`; : `#${color}`;
}); });
} }
}
<span>{{this.label}}</span> <template>
<span>{{this.label}}</span>
</template>
}

View File

@ -1,24 +1,28 @@
import { readOnly } from "@ember/object/computed"; import { readOnly } from "@ember/object/computed";
import { htmlSafe } from "@ember/template";
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("dropdown-select-box-row") @classNames("dropdown-select-box-row")
export default class DropdownSelectBoxRow extends SelectKitRowComponent { export default class DropdownSelectBoxRow extends SelectKitRowComponent {
@readOnly("item.description") description; @readOnly("item.description") description;
<template>
{{#if this.icons}}
<div class="icons">
<span class="selection-indicator"></span>
{{#each this.icons as |i|}}
{{icon i}}
{{/each}}
</div>
{{/if}}
<div class="texts">
<span class="name">{{htmlSafe this.label}}</span>
{{#if this.description}}
<span class="desc">{{htmlSafe this.description}}</span>
{{/if}}
</div>
</template>
} }
{{#if this.icons}}
<div class="icons">
<span class="selection-indicator"></span>
{{#each this.icons as |icon|}}
{{d-icon icon}}
{{/each}}
</div>
{{/if}}
<div class="texts">
<span class="name">{{html-safe this.label}}</span>
{{#if this.description}}
<span class="desc">{{html-safe this.description}}</span>
{{/if}}
</div>

View File

@ -1,26 +1,37 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import { and } from "truth-helpers";
import UserStatusMessage from "discourse/components/user-status-message";
import avatar from "discourse/helpers/avatar";
import icon from "discourse/helpers/d-icon";
import decorateUsernameSelector from "discourse/helpers/decorate-username-selector";
import formatUsername from "discourse/helpers/format-username";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("email-group-user-chooser-row") @classNames("email-group-user-chooser-row")
export default class EmailGroupUserChooserRow extends SelectKitRowComponent {} export default class EmailGroupUserChooserRow extends SelectKitRowComponent {
<template>
{{#if this.item.isUser}} {{#if this.item.isUser}}
{{avatar this.item imageSize="tiny"}} {{avatar this.item imageSize="tiny"}}
<div> <div>
<span class="identifier">{{format-username this.item.id}}</span> <span class="identifier">{{formatUsername this.item.id}}</span>
<span class="name">{{this.item.name}}</span> <span class="name">{{this.item.name}}</span>
</div> </div>
{{#if (and this.item.showUserStatus this.item.status)}} {{#if (and this.item.showUserStatus this.item.status)}}
<UserStatusMessage @status={{this.item.status}} @showDescription={{true}} /> <UserStatusMessage
{{/if}} @status={{this.item.status}}
{{decorate-username-selector this.item.id}} @showDescription={{true}}
{{else if this.item.isGroup}} />
{{d-icon "users"}} {{/if}}
<div> {{decorateUsernameSelector this.item.id}}
<span class="identifier">{{this.item.id}}</span> {{else if this.item.isGroup}}
<span class="name">{{this.item.full_name}}</span> {{icon "users"}}
</div> <div>
{{else}} <span class="identifier">{{this.item.id}}</span>
{{d-icon "envelope"}} <span class="name">{{this.item.full_name}}</span>
<span class="identifier">{{this.item.id}}</span> </div>
{{/if}} {{else}}
{{icon "envelope"}}
<span class="identifier">{{this.item.id}}</span>
{{/if}}
</template>
}

View File

@ -1,16 +1,19 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import AvatarFlair from "discourse/components/avatar-flair";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("flair-row") @classNames("flair-row")
export default class FlairRow extends SelectKitRowComponent {} export default class FlairRow extends SelectKitRowComponent {
<template>
{{#if this.item.url}}
<AvatarFlair
@flairName={{this.item.name}}
@flairUrl={{this.item.url}}
@flairBgColor={{this.item.bgColor}}
@flairColor={{this.item.color}}
/>
{{/if}}
{{#if this.item.url}} <span>{{this.label}}</span>
<AvatarFlair </template>
@flairName={{this.item.name}} }
@flairUrl={{this.item.url}}
@flairBgColor={{this.item.bgColor}}
@flairColor={{this.item.color}}
/>
{{/if}}
<span>{{this.label}}</span>

View File

@ -1,19 +1,22 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("future-date-input-selector-row") @classNames("future-date-input-selector-row")
export default class FutureDateInputSelectorRow extends SelectKitRowComponent {} export default class FutureDateInputSelectorRow extends SelectKitRowComponent {
<template>
{{#if this.item.icon}}
<div class="future-date-input-selector-icons">
{{icon this.item.icon}}
</div>
{{/if}}
{{#if this.item.icon}} <span class="name">{{this.label}}</span>
<div class="future-date-input-selector-icons">
{{d-icon this.item.icon}}
</div>
{{/if}}
<span class="name">{{this.label}}</span> {{#if this.item.timeFormatted}}
<span class="future-date-input-selector-datetime">
{{#if this.item.timeFormatted}} {{this.item.timeFormatted}}
<span class="future-date-input-selector-datetime"> </span>
{{this.item.timeFormatted}} {{/if}}
</span> </template>
{{/if}} }

View File

@ -1,12 +1,15 @@
import { htmlSafe } from "@ember/template";
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("homepage-style-selector-row") @classNames("homepage-style-selector-row")
export default class HomepageStyleSelectorRow extends SelectKitRowComponent {} export default class HomepageStyleSelectorRow extends SelectKitRowComponent {
<template>
<div class="texts"> <div class="texts">
<span class="name">{{html-safe this.label}}</span> <span class="name">{{htmlSafe this.label}}</span>
{{#if this.item.description}} {{#if this.item.description}}
<span class="desc">{{html-safe this.item.description}}</span> <span class="desc">{{htmlSafe this.item.description}}</span>
{{/if}} {{/if}}
</div> </div>
</template>
}

View File

@ -1,7 +1,10 @@
import Component from "@ember/component"; import Component from "@ember/component";
import { fn } from "@ember/helper";
import { computed } from "@ember/object"; import { computed } from "@ember/object";
import { reads } from "@ember/object/computed"; import { reads } from "@ember/object/computed";
import { tagName } from "@ember-decorators/component"; import { tagName } from "@ember-decorators/component";
import DButton from "discourse/components/d-button";
import discourseTag from "discourse/helpers/discourse-tag";
@tagName("") @tagName("")
export default class SelectedCollection extends Component { export default class SelectedCollection extends Component {
@ -27,20 +30,22 @@ export default class SelectedCollection extends Component {
}; };
}); });
} }
}
{{#if this.tags}} <template>
<div class="mini-tag-chooser-selected-collection selected-tags"> {{#if this.tags}}
{{#each this.tags as |tag|}} <div class="mini-tag-chooser-selected-collection selected-tags">
<DButton {{#each this.tags as |tag|}}
@translatedTitle={{tag.value}} <DButton
@icon="xmark" @translatedTitle={{tag.value}}
@action={{fn (action this.selectKit.deselect) tag.value}} @icon="xmark"
tabindex="0" @action={{fn (action this.selectKit.deselect) tag.value}}
class={{tag.classNames}} tabindex="0"
> class={{tag.classNames}}
{{discourse-tag tag.value noHref=true}} >
</DButton> {{discourseTag tag.value noHref=true}}
{{/each}} </DButton>
</div> {{/each}}
{{/if}} </div>
{{/if}}
</template>
}

View File

@ -21,8 +21,10 @@ export default class FormatSelectedContent extends Component.extend(
return this.getName(this.selectKit.noneItem); return this.getName(this.selectKit.noneItem);
} }
} }
}
<span class="formatted-selection"> <template>
{{this.formattedContent}} <span class="formatted-selection">
</span> {{this.formattedContent}}
</span>
</template>
}

View File

@ -1,6 +1,9 @@
import { Input } from "@ember/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { isEmpty } from "@ember/utils"; import { isEmpty } from "@ember/utils";
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import SelectKitFilterComponent from "select-kit/components/select-kit/select-kit-filter"; import SelectKitFilterComponent from "select-kit/components/select-kit/select-kit-filter";
@ -34,30 +37,32 @@ export default class MultiSelectFilter extends SelectKitFilterComponent {
return false; return false;
} }
} }
<template>
{{#unless this.isHidden}}
{{! filter-input-search prevents 1password from attempting autocomplete }}
{{! template-lint-disable no-pointer-down-event-binding }}
<Input
tabindex={{0}}
class="filter-input"
placeholder={{this.computedPlaceholder}}
autocomplete="off"
autocorrect="off"
autocapitalize="off"
name="filter-input-search"
spellcheck={{false}}
@value={{readonly this.selectKit.filter}}
@type="search"
{{on "paste" (action "onPaste")}}
{{on "keydown" (action "onKeydown")}}
{{on "keyup" (action "onKeyup")}}
{{on "input" (action "onInput")}}
/>
{{#if this.selectKit.options.filterIcon}}
{{icon this.selectKit.options.filterIcon class="filter-icon"}}
{{/if}}
{{/unless}}
</template>
} }
{{#unless this.isHidden}}
{{! filter-input-search prevents 1password from attempting autocomplete }}
{{! template-lint-disable no-pointer-down-event-binding }}
<Input
tabindex={{0}}
class="filter-input"
placeholder={{this.computedPlaceholder}}
autocomplete="off"
autocorrect="off"
autocapitalize="off"
name="filter-input-search"
spellcheck={{false}}
@value={{readonly this.selectKit.filter}}
@type="search"
{{on "paste" (action "onPaste")}}
{{on "keydown" (action "onKeydown")}}
{{on "keyup" (action "onKeyup")}}
{{on "input" (action "onInput")}}
/>
{{#if this.selectKit.options.filterIcon}}
{{d-icon this.selectKit.options.filterIcon class="filter-icon"}}
{{/if}}
{{/unless}}

View File

@ -1,7 +1,9 @@
import { on } from "@ember/modifier";
import { computed } from "@ember/object"; import { computed } from "@ember/object";
import { htmlSafe } from "@ember/template"; import { htmlSafe } from "@ember/template";
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import { categoryBadgeHTML } from "discourse/helpers/category-link"; import { categoryBadgeHTML } from "discourse/helpers/category-link";
import icon from "discourse/helpers/d-icon";
import SelectedNameComponent from "select-kit/components/selected-name"; import SelectedNameComponent from "select-kit/components/selected-name";
@classNames("selected-category") @classNames("selected-category")
@ -15,18 +17,20 @@ export default class SelectedCategory extends SelectedNameComponent {
}) })
); );
} }
}
<div <template>
{{on "click" this.onSelectedNameClick}} <div
tabindex="0" {{on "click" this.onSelectedNameClick}}
title={{this.title}} tabindex="0"
data-value={{this.value}} title={{this.title}}
data-name={{this.name}} data-value={{this.value}}
class="select-kit-selected-name selected-name choice" data-name={{this.name}}
> class="select-kit-selected-name selected-name choice"
<div class="body"> >
{{this.badge}} <div class="body">
{{d-icon "xmark"}} {{this.badge}}
</div> {{icon "xmark"}}
</div> </div>
</div>
</template>
}

View File

@ -1,6 +1,7 @@
import { htmlSafe } from "@ember/template"; import { htmlSafe } from "@ember/template";
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import { categoryBadgeHTML } from "discourse/helpers/category-link"; import { categoryBadgeHTML } from "discourse/helpers/category-link";
import dirSpan from "discourse/helpers/dir-span";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import CategoryRowComponent from "select-kit/components/category-row"; import CategoryRowComponent from "select-kit/components/category-row";
@ -16,24 +17,26 @@ export default class NoneCategoryRow extends CategoryRowComponent {
}) })
); );
} }
}
{{#if this.category}} <template>
<div class="category-status" aria-hidden="true"> {{#if this.category}}
{{#if this.hasParentCategory}} <div class="category-status" aria-hidden="true">
{{#unless this.hideParentCategory}} {{#if this.hasParentCategory}}
{{this.badgeForParentCategory}} {{#unless this.hideParentCategory}}
{{/unless}} {{this.badgeForParentCategory}}
{{/unless}}
{{/if}}
{{this.badgeForCategory}}
</div>
{{#if this.shouldDisplayDescription}}
<div class="category-desc" aria-hidden="true">{{dirSpan
this.descriptionText
htmlSafe="true"
}}</div>
{{/if}}
{{else}}
{{htmlSafe this.label}}
{{/if}} {{/if}}
{{this.badgeForCategory}} </template>
</div> }
{{#if this.shouldDisplayDescription}}
<div class="category-desc" aria-hidden="true">{{dir-span
this.descriptionText
htmlSafe="true"
}}</div>
{{/if}}
{{else}}
{{html-safe this.label}}
{{/if}}

View File

@ -1,6 +1,8 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import { fmt } from "discourse/lib/computed"; import { fmt } from "discourse/lib/computed";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n";
import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header"; import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header";
@classNames("notifications-filter-header", "btn-flat") @classNames("notifications-filter-header", "btn-flat")
@ -11,14 +13,16 @@ export default class NotificationsFilterHeader extends DropdownSelectBoxHeaderCo
caretIcon(isExpanded) { caretIcon(isExpanded) {
return isExpanded ? "caret-up" : "caret-down"; return isExpanded ? "caret-up" : "caret-down";
} }
}
<div class="select-kit-header-wrapper"> <template>
<span class="filter-text"> <div class="select-kit-header-wrapper">
{{i18n "user.user_notifications.filters.filter_by"}} <span class="filter-text">
</span> {{i18n "user.user_notifications.filters.filter_by"}}
<span class="header-text"> </span>
{{i18n this.label}} <span class="header-text">
</span> {{i18n this.label}}
{{d-icon this.caretIcon class="caret-icon"}} </span>
</div> {{icon this.caretIcon class="caret-icon"}}
</div>
</template>
}

View File

@ -1,4 +1,6 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import periodTitle from "discourse/helpers/period-title";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header"; import DropdownSelectBoxHeaderComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-header";
@ -8,14 +10,16 @@ export default class PeriodChooserHeader extends DropdownSelectBoxHeaderComponen
caretIcon(isExpanded) { caretIcon(isExpanded) {
return isExpanded ? "caret-up" : "caret-down"; return isExpanded ? "caret-up" : "caret-down";
} }
<template>
<h2 class="selected-name" title={{this.title}}>
{{periodTitle
this.value
showDateRange=true
fullDay=this.selectKit.options.fullDay
}}
</h2>
{{icon this.caretIcon class="caret-icon"}}
</template>
} }
<h2 class="selected-name" title={{this.title}}>
{{period-title
this.value
showDateRange=true
fullDay=this.selectKit.options.fullDay
}}
</h2>
{{d-icon this.caretIcon class="caret-icon"}}

View File

@ -1,4 +1,5 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import periodTitle from "discourse/helpers/period-title";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import DropdownSelectBoxRowComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-row"; import DropdownSelectBoxRowComponent from "select-kit/components/dropdown-select-box/dropdown-select-box-row";
@ -9,14 +10,16 @@ export default class PeriodChooserRow extends DropdownSelectBoxRowComponent {
title(rowName) { title(rowName) {
return i18n(`filters.top.${rowName || "this_week"}`).title; return i18n(`filters.top.${rowName || "this_week"}`).title;
} }
<template>
<span class="selection-indicator"></span>
<span class="period-title">
{{periodTitle
this.rowValue
showDateRange=true
fullDay=this.selectKit.options.fullDay
}}
</span>
</template>
} }
<span class="selection-indicator"></span>
<span class="period-title">
{{period-title
this.rowValue
showDateRange=true
fullDay=this.selectKit.options.fullDay
}}
</span>

View File

@ -2,12 +2,14 @@ import Component from "@ember/component";
import { tagName } from "@ember-decorators/component"; import { tagName } from "@ember-decorators/component";
@tagName("") @tagName("")
export default class ErrorsCollection extends Component {} export default class ErrorsCollection extends Component {
<template>
{{#if this.collection.content}} {{#if this.collection.content}}
<ul class="select-kit-errors-collection"> <ul class="select-kit-errors-collection">
{{#each this.collection.content as |item|}} {{#each this.collection.content as |item|}}
<li class="select-kit-error">{{item}}</li> <li class="select-kit-error">{{item}}</li>
{{/each}} {{/each}}
</ul> </ul>
{{/if}} {{/if}}
</template>
}

View File

@ -61,8 +61,10 @@ export default class SelectKitBody extends Component {
this.selectKit.close(event); this.selectKit.close(event);
}); });
} }
}
{{#if this.selectKit.isExpanded}} <template>
{{yield}} {{#if this.selectKit.isExpanded}}
{{/if}} {{yield}}
{{/if}}
</template>
}

View File

@ -1,13 +1,16 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("create") @classNames("create")
export default class SelectKitCreateRow extends SelectKitRowComponent {} export default class SelectKitCreateRow extends SelectKitRowComponent {
<template>
{{#each this.icons as |i|}}
{{icon i translatedTitle=this.dasherizedTitle}}
{{/each}}
{{#each this.icons as |icon|}} <span class="name">
{{d-icon icon translatedTitle=this.dasherizedTitle}} {{this.label}}
{{/each}} </span>
</template>
<span class="name"> }
{{this.label}}
</span>

View File

@ -1,4 +1,5 @@
import Component from "@ember/component"; import Component, { Input } from "@ember/component";
import { on } from "@ember/modifier";
import { action, computed } from "@ember/object"; import { action, computed } from "@ember/object";
import { not } from "@ember/object/computed"; import { not } from "@ember/object/computed";
import { isPresent } from "@ember/utils"; import { isPresent } from "@ember/utils";
@ -7,6 +8,7 @@ import {
classNameBindings, classNameBindings,
classNames, classNames,
} from "@ember-decorators/component"; } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import UtilsMixin from "select-kit/mixins/utils"; import UtilsMixin from "select-kit/mixins/utils";
@ -137,30 +139,32 @@ export default class SelectKitFilter extends Component.extend(UtilsMixin) {
this.selectKit.set("highlighted", null); this.selectKit.set("highlighted", null);
} }
<template>
{{#unless this.isHidden}}
{{! filter-input-search prevents 1password from attempting autocomplete }}
{{! template-lint-disable no-pointer-down-event-binding }}
<Input
tabindex={{0}}
class="filter-input"
placeholder={{this.placeholder}}
autocomplete="off"
autocorrect="off"
autocapitalize="off"
name="filter-input-search"
spellcheck={{false}}
@value={{readonly this.selectKit.filter}}
@type="search"
{{on "paste" (action "onPaste")}}
{{on "keydown" (action "onKeydown")}}
{{on "keyup" (action "onKeyup")}}
{{on "input" (action "onInput")}}
/>
{{#if this.selectKit.options.filterIcon}}
{{icon this.selectKit.options.filterIcon class="filter-icon"}}
{{/if}}
{{/unless}}
</template>
} }
{{#unless this.isHidden}}
{{! filter-input-search prevents 1password from attempting autocomplete }}
{{! template-lint-disable no-pointer-down-event-binding }}
<Input
tabindex={{0}}
class="filter-input"
placeholder={{this.placeholder}}
autocomplete="off"
autocorrect="off"
autocapitalize="off"
name="filter-input-search"
spellcheck={{false}}
@value={{readonly this.selectKit.filter}}
@type="search"
{{on "paste" (action "onPaste")}}
{{on "keydown" (action "onKeydown")}}
{{on "keyup" (action "onKeyup")}}
{{on "input" (action "onInput")}}
/>
{{#if this.selectKit.options.filterIcon}}
{{d-icon this.selectKit.options.filterIcon class="filter-icon"}}
{{/if}}
{{/unless}}

View File

@ -1,13 +1,16 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("none") @classNames("none")
export default class SelectKitNoneRow extends SelectKitRowComponent {} export default class SelectKitNoneRow extends SelectKitRowComponent {
<template>
{{#each this.icons as |i|}}
{{icon i translatedTitle=this.dasherizedTitle}}
{{/each}}
{{#each this.icons as |icon|}} <span class="name">
{{d-icon icon translatedTitle=this.dasherizedTitle}} {{this.label}}
{{/each}} </span>
</template>
<span class="name"> }
{{this.label}}
</span>

View File

@ -9,6 +9,7 @@ import {
classNames, classNames,
tagName, tagName,
} from "@ember-decorators/component"; } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import { makeArray } from "discourse/lib/helpers"; import { makeArray } from "discourse/lib/helpers";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import UtilsMixin from "select-kit/mixins/utils"; import UtilsMixin from "select-kit/mixins/utils";
@ -117,9 +118,9 @@ export default class SelectKitRow extends Component.extend(UtilsMixin) {
@computed("item.{icon,icons}") @computed("item.{icon,icons}")
get icons() { get icons() {
const icon = makeArray(this.getProperty(this.item, "icon")); const _icon = makeArray(this.getProperty(this.item, "icon"));
const icons = makeArray(this.getProperty(this.item, "icons")); const icons = makeArray(this.getProperty(this.item, "icons"));
return icon.concat(icons).filter(Boolean); return _icon.concat(icons).filter(Boolean);
} }
@computed("selectKit.highlighted") @computed("selectKit.highlighted")
@ -203,12 +204,14 @@ export default class SelectKitRow extends Component.extend(UtilsMixin) {
} }
} }
} }
<template>
{{#each this.icons as |i|}}
{{icon i translatedTitle=this.dasherizedTitle}}
{{/each}}
<span class="name">
{{this.label}}
</span>
</template>
} }
{{#each this.icons as |icon|}}
{{d-icon icon translatedTitle=this.dasherizedTitle}}
{{/each}}
<span class="name">
{{this.label}}
</span>

View File

@ -1,7 +1,11 @@
import Component from "@ember/component"; import Component from "@ember/component";
import { fn } from "@ember/helper";
import { on } from "@ember/modifier";
import { computed } from "@ember/object"; import { computed } from "@ember/object";
import { guidFor } from "@ember/object/internals"; import { guidFor } from "@ember/object/internals";
import { tagName } from "@ember-decorators/component"; import { tagName } from "@ember-decorators/component";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
import UtilsMixin from "select-kit/mixins/utils"; import UtilsMixin from "select-kit/mixins/utils";
@tagName("") @tagName("")
@ -39,30 +43,32 @@ export default class SelectedChoice extends Component.extend(UtilsMixin) {
} }
return this.mandatoryValuesArray.includes(this.item.id); return this.mandatoryValuesArray.includes(this.item.id);
} }
}
{{#if this.readOnly}} <template>
<button {{#if this.readOnly}}
class="btn btn-default disabled" <button
title={{I18n "admin.site_settings.mandatory_group"}} class="btn btn-default disabled"
>{{this.itemName}}</button> title={{i18n "admin.site_settings.mandatory_group"}}
{{else}} >{{this.itemName}}</button>
<button
{{on "click" (fn this.selectKit.deselect this.item)}}
aria-label={{i18n "select_kit.delete_item" name=this.itemName}}
data-value={{this.itemValue}}
data-name={{this.itemName}}
type="button"
id="{{this.id}}-choice"
class="btn btn-default selected-choice {{this.extraClass}}"
>
{{d-icon "xmark"}}
{{#if (has-block)}}
{{yield}}
{{else}} {{else}}
<span class="d-button-label"> <button
{{this.itemName}} {{on "click" (fn this.selectKit.deselect this.item)}}
</span> aria-label={{i18n "select_kit.delete_item" name=this.itemName}}
data-value={{this.itemValue}}
data-name={{this.itemName}}
type="button"
id="{{this.id}}-choice"
class="btn btn-default selected-choice {{this.extraClass}}"
>
{{icon "xmark"}}
{{#if (has-block)}}
{{yield}}
{{else}}
<span class="d-button-label">
{{this.itemName}}
</span>
{{/if}}
</button>
{{/if}} {{/if}}
</button> </template>
{{/if}} }

View File

@ -1,16 +1,19 @@
import { tagName } from "@ember-decorators/component"; import { tagName } from "@ember-decorators/component";
import AvatarFlair from "discourse/components/avatar-flair";
import SelectedNameComponent from "select-kit/components/selected-name"; import SelectedNameComponent from "select-kit/components/selected-name";
@tagName("") @tagName("")
export default class SelectedFlair extends SelectedNameComponent {} export default class SelectedFlair extends SelectedNameComponent {
<template>
{{#if this.item.url}}
<AvatarFlair
@flairName={{this.item.name}}
@flairUrl={{this.item.url}}
@flairBgColor={{this.item.bgColor}}
@flairColor={{this.item.color}}
/>
{{/if}}
{{#if this.item.url}} <span>{{this.label}}</span>
<AvatarFlair </template>
@flairName={{this.item.name}} }
@flairUrl={{this.item.url}}
@flairBgColor={{this.item.bgColor}}
@flairColor={{this.item.color}}
/>
{{/if}}
<span>{{this.label}}</span>

View File

@ -1,8 +1,12 @@
import Component from "@ember/component"; import Component from "@ember/component";
import { fn } from "@ember/helper";
import { computed, get } from "@ember/object"; import { computed, get } from "@ember/object";
import { reads } from "@ember/object/computed"; import { reads } from "@ember/object/computed";
import { guidFor } from "@ember/object/internals"; import { guidFor } from "@ember/object/internals";
import { tagName } from "@ember-decorators/component"; import { tagName } from "@ember-decorators/component";
import { and } from "truth-helpers";
import DButton from "discourse/components/d-button";
import icon from "discourse/helpers/d-icon";
import { makeArray } from "discourse/lib/helpers"; import { makeArray } from "discourse/lib/helpers";
import UtilsMixin from "select-kit/mixins/utils"; import UtilsMixin from "select-kit/mixins/utils";
@ -77,9 +81,9 @@ export default class SelectedName extends Component.extend(UtilsMixin) {
@computed("item.{icon,icons}") @computed("item.{icon,icons}")
get icons() { get icons() {
const icon = makeArray(this._safeProperty("icon", this.item)); const _icon = makeArray(this._safeProperty("icon", this.item));
const icons = makeArray(this._safeProperty("icons", this.item)); const icons = makeArray(this._safeProperty("icons", this.item));
return icon.concat(icons).filter(Boolean); return _icon.concat(icons).filter(Boolean);
} }
_safeProperty(name, content) { _safeProperty(name, content) {
@ -89,48 +93,50 @@ export default class SelectedName extends Component.extend(UtilsMixin) {
return get(content, name); return get(content, name);
} }
<template>
{{#if this.selectKit.options.showFullTitle}}
<div
lang={{this.lang}}
title={{this.title}}
data-value={{this.value}}
data-name={{this.name}}
class="select-kit-selected-name selected-name choice"
>
{{#if this.selectKit.options.formName}}
<input
type="hidden"
name={{this.selectKit.options.formName}}
value={{this.value}}
/>
{{/if}}
{{#if (and this.renderIcon this.item.icon)}}
{{icon this.item.icon}}
{{/if}}
<span class="name">
{{this.label}}
</span>
{{#if this.shouldDisplayClearableButton}}
<DButton
@icon="xmark"
@action={{fn this.selectKit.deselect this.item}}
@ariaLabel="clear_input"
class="btn-clear"
/>
{{/if}}
</div>
{{else}}
{{#if this.item.icon}}
<div
lang={{this.lang}}
class="select-kit-selected-name selected-name choice"
>
{{icon this.item.icon}}
</div>
{{/if}}
{{/if}}
</template>
} }
{{#if this.selectKit.options.showFullTitle}}
<div
lang={{this.lang}}
title={{this.title}}
data-value={{this.value}}
data-name={{this.name}}
class="select-kit-selected-name selected-name choice"
>
{{#if this.selectKit.options.formName}}
<input
type="hidden"
name={{this.selectKit.options.formName}}
value={{this.value}}
/>
{{/if}}
{{#if (and this.renderIcon this.item.icon)}}
{{d-icon this.item.icon}}
{{/if}}
<span class="name">
{{this.label}}
</span>
{{#if this.shouldDisplayClearableButton}}
<DButton
@icon="xmark"
@action={{fn this.selectKit.deselect this.item}}
@ariaLabel="clear_input"
class="btn-clear"
/>
{{/if}}
</div>
{{else}}
{{#if this.item.icon}}
<div
lang={{this.lang}}
class="select-kit-selected-name selected-name choice"
>
{{d-icon this.item.icon}}
</div>
{{/if}}
{{/if}}

View File

@ -1,7 +1,10 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import discourseTag from "discourse/helpers/discourse-tag";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("tag-chooser-row") @classNames("tag-chooser-row")
export default class TagChooserRow extends SelectKitRowComponent {} export default class TagChooserRow extends SelectKitRowComponent {
<template>
{{discourse-tag this.rowValue count=this.item.count noHref=true}} {{discourseTag this.rowValue count=this.item.count noHref=true}}
</template>
}

View File

@ -1,4 +1,5 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import discourseTag from "discourse/helpers/discourse-tag";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@ -8,15 +9,17 @@ export default class TagRow extends SelectKitRowComponent {
isTag(item) { isTag(item) {
return item.id !== "no-tags" && item.id !== "all-tags"; return item.id !== "no-tags" && item.id !== "all-tags";
} }
}
{{#if this.isTag}} <template>
{{discourse-tag {{#if this.isTag}}
this.rowValue {{discourseTag
noHref=true this.rowValue
description=this.item.description noHref=true
count=this.item.count description=this.item.description
}} count=this.item.count
{{else}} }}
<span class="name">{{this.item.name}}</span> {{else}}
{{/if}} <span class="name">{{this.item.name}}</span>
{{/if}}
</template>
}

View File

@ -1,5 +1,5 @@
import Component from "@ember/component"; import Component from "@ember/component";
export default class ToolbarPopupMenuOptionsHeading extends Component {} export default class ToolbarPopupMenuOptionsHeading extends Component {
<template>{{this.heading}}</template>
{{this.heading}} }

View File

@ -1,16 +1,21 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import TopicStatus from "discourse/components/topic-status";
import boundCategoryLink from "discourse/helpers/bound-category-link";
import replaceEmoji from "discourse/helpers/replace-emoji";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("topic-row") @classNames("topic-row")
export default class TopicRow extends SelectKitRowComponent {} export default class TopicRow extends SelectKitRowComponent {
<template>
<TopicStatus @topic={{this.item}} @disableActions={{true}} /> <TopicStatus @topic={{this.item}} @disableActions={{true}} />
<div class="topic-title">{{replace-emoji this.item.title}}</div> <div class="topic-title">{{replaceEmoji this.item.title}}</div>
<div class="topic-categories"> <div class="topic-categories">
{{bound-category-link {{boundCategoryLink
this.item.category this.item.category
ancestors=this.item.category.predecessors ancestors=this.item.category.predecessors
hideParent=true hideParent=true
link=false link=false
}} }}
</div> </div>
</template>
}

View File

@ -1,13 +1,17 @@
import { classNames } from "@ember-decorators/component"; import { classNames } from "@ember-decorators/component";
import avatar from "discourse/helpers/avatar";
import formatUsername from "discourse/helpers/format-username";
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row"; import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
@classNames("user-row") @classNames("user-row")
export default class UserRow extends SelectKitRowComponent {} export default class UserRow extends SelectKitRowComponent {
<template>
{{avatar this.item imageSize="tiny"}}
{{avatar this.item imageSize="tiny"}} <span class="username">{{formatUsername this.item.username}}</span>
<span class="username">{{format-username this.item.username}}</span> {{#if this.item.name}}
<span class="name">{{this.item.name}}</span>
{{#if this.item.name}} {{/if}}
<span class="name">{{this.item.name}}</span> </template>
{{/if}} }

View File

@ -20,7 +20,8 @@
"ember-auto-import": "^2.10.0", "ember-auto-import": "^2.10.0",
"ember-cli-babel": "^8.2.0", "ember-cli-babel": "^8.2.0",
"ember-cli-htmlbars": "^6.3.0", "ember-cli-htmlbars": "^6.3.0",
"ember-template-imports": "^4.3.0" "ember-template-imports": "^4.3.0",
"truth-helpers": "workspace:*"
}, },
"devDependencies": { "devDependencies": {
"@ember/optional-features": "^2.2.0", "@ember/optional-features": "^2.2.0",

View File

@ -3,7 +3,7 @@ import I18n, { i18n } from "discourse-i18n";
import DaBooleanField from "./fields/da-boolean-field"; import DaBooleanField from "./fields/da-boolean-field";
import DaCategoriesField from "./fields/da-categories-field"; import DaCategoriesField from "./fields/da-categories-field";
import DaCategoryField from "./fields/da-category-field"; import DaCategoryField from "./fields/da-category-field";
import DaCategoryNotificationlevelField from "./fields/da-category-notification-level-field"; import DaCategoryNotificationLevelField from "./fields/da-category-notification-level-field";
import DaChoicesField from "./fields/da-choices-field"; import DaChoicesField from "./fields/da-choices-field";
import DaCustomField from "./fields/da-custom-field"; import DaCustomField from "./fields/da-custom-field";
import DaCustomFields from "./fields/da-custom-fields"; import DaCustomFields from "./fields/da-custom-fields";
@ -44,7 +44,7 @@ const FIELD_COMPONENTS = {
group: DaGroupField, group: DaGroupField,
groups: DaGroupsField, groups: DaGroupsField,
choices: DaChoicesField, choices: DaChoicesField,
category_notification_level: DaCategoryNotificationlevelField, category_notification_level: DaCategoryNotificationLevelField,
email_group_user: DaEmailGroupUserField, email_group_user: DaEmailGroupUserField,
custom_field: DaCustomField, custom_field: DaCustomField,
custom_fields: DaCustomFields, custom_fields: DaCustomFields,

View File

@ -3,7 +3,7 @@ import BaseField from "./da-base-field";
import DAFieldDescription from "./da-field-description"; import DAFieldDescription from "./da-field-description";
import DAFieldLabel from "./da-field-label"; import DAFieldLabel from "./da-field-label";
export default class CategoryNotficationLevelField extends BaseField { export default class CategoryNotificationLevelField extends BaseField {
<template> <template>
<section class="field category-notification-level-field"> <section class="field category-notification-level-field">
<div class="control-group"> <div class="control-group">

View File

@ -0,0 +1,10 @@
import htmlSafe from "discourse/helpers/html-safe";
const FormError = <template>
{{#if @error}}
<div class="alert alert-error form-errors">
{{htmlSafe @error}}
</div>
{{/if}}
</template>;
export default FormError;

View File

@ -0,0 +1,22 @@
import { Input } from "@ember/component";
import { on } from "@ember/modifier";
import { i18n } from "discourse-i18n";
const TopicTrigger = <template>
<div class="control-group">
<label class="control-label">
{{i18n "discourse_automation.triggerables.topic.topic_id.label"}}
</label>
<div class="controls">
<Input
@value={{this.metadata.topic_id}}
{{on
"input"
(action (mut this.metadata.topic_id) value="target.value")
}}
/>
</div>
</div>
</template>;
export default TopicTrigger;

View File

@ -1,5 +0,0 @@
{{#if @error}}
<div class="alert alert-error form-errors">
{{html-safe @error}}
</div>
{{/if}}

View File

@ -1,12 +0,0 @@
<div class="control-group">
<label class="control-label">
{{i18n "discourse_automation.triggerables.topic.topic_id.label"}}
</label>
<div class="controls">
<Input
@value={{this.metadata.topic_id}}
{{on "input" (action (mut this.metadata.topic_id) value="target.value")}}
/>
</div>
</div>

View File

@ -1,189 +1,228 @@
<div class="admin-detail discourse-automation-edit discourse-automation-form"> import { Input } from "@ember/component";
<BackButton import { fn, hash } from "@ember/helper";
@label="discourse_automation.back" import { on } from "@ember/modifier";
@route="adminPlugins.show.automation.index" import RouteTemplate from "ember-route-template";
class="discourse-automation-back" import { and } from "truth-helpers";
/> import BackButton from "discourse/components/back-button";
<AdminConfigAreaCard @heading="discourse_automation.select_script"> import DButton from "discourse/components/d-button";
<:content> import TextField from "discourse/components/text-field";
<form class="form-horizontal"> import withEventValue from "discourse/helpers/with-event-value";
<FormError @error={{this.error}} /> import { i18n } from "discourse-i18n";
import AdminConfigAreaCard from "admin/components/admin-config-area-card";
import ComboBox from "select-kit/components/combo-box";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import FormError from "discourse/plugins/automation/admin/components/form-error";
<section class="form-section edit"> export default RouteTemplate(
<div class="control-group"> <template>
<label class="control-label"> <div
{{i18n "discourse_automation.models.automation.name.label"}} class="admin-detail discourse-automation-edit discourse-automation-form"
</label> >
<BackButton
@label="discourse_automation.back"
@route="adminPlugins.show.automation.index"
class="discourse-automation-back"
/>
<AdminConfigAreaCard @heading="discourse_automation.select_script">
<:content>
<form class="form-horizontal">
<FormError @error={{@controller.error}} />
<div class="controls"> <section class="form-section edit">
<TextField <div class="control-group">
@value={{this.automationForm.name}} <label class="control-label">
@type="text" {{i18n "discourse_automation.models.automation.name.label"}}
@autofocus={{true}} </label>
@name="automation-name"
class="input-large"
@input={{with-event-value (fn (mut this.automationForm.name))}}
/>
</div>
</div>
<div class="control-group"> <div class="controls">
<label class="control-label"> <TextField
{{i18n "discourse_automation.models.script.name.label"}} @value={{@controller.automationForm.name}}
</label> @type="text"
@autofocus={{true}}
<div class="controls"> @name="automation-name"
<ComboBox class="input-large"
@value={{this.automationForm.script}} @input={{withEventValue
@content={{this.model.scriptables}} (fn (mut @controller.automationForm.name))
@onChange={{this.onChangeScript}}
@options={{hash filterable=true}}
class="scriptables"
/>
</div>
</div>
</section>
<section class="trigger-section form-section edit">
<h2 class="title">
{{i18n
"discourse_automation.edit_automation.trigger_section.title"
}}
</h2>
<div class="control-group">
{{#if this.model.automation.script.forced_triggerable}}
<div class="alert alert-warning">
{{i18n
"discourse_automation.edit_automation.trigger_section.forced"
}}
</div>
{{/if}}
<label class="control-label">
{{i18n "discourse_automation.models.trigger.name.label"}}
</label>
<div class="controls">
<ComboBox
@value={{this.automationForm.trigger}}
@content={{this.model.triggerables}}
@onChange={{this.onChangeTrigger}}
@options={{hash
filterable=true
none="discourse_automation.select_trigger"
disabled=this.model.automation.script.forced_triggerable
}}
class="triggerables"
/>
</div>
</div>
{{#if this.automationForm.trigger}}
{{#if this.model.automation.trigger.doc}}
<div class="alert alert-info">
<p>{{this.model.automation.trigger.doc}}</p>
</div>
{{/if}}
{{#if
(and
this.model.automation.enabled
this.model.automation.trigger.settings.manual_trigger
)
}}
<div class="alert alert-info next-trigger">
{{#if this.nextPendingAutomationAtFormatted}}
<p>
{{i18n
"discourse_automation.edit_automation.trigger_section.next_pending_automation"
date=this.nextPendingAutomationAtFormatted
}} }}
</p> />
{{/if}} </div>
<DButton
@label="discourse_automation.edit_automation.trigger_section.trigger_now"
@isLoading={{this.isTriggeringAutomation}}
@action={{fn
this.onManualAutomationTrigger
this.model.automation.id
}}
class="btn-primary trigger-now-btn"
/>
</div> </div>
{{/if}}
{{#each this.triggerFields as |field|}} <div class="control-group">
<AutomationField <label class="control-label">
@automation={{this.automation}} {{i18n "discourse_automation.models.script.name.label"}}
@field={{field}} </label>
@saveAutomation={{fn this.saveAutomation this.automation}}
/>
{{/each}}
{{/if}}
</section>
{{#if this.automationForm.trigger}} <div class="controls">
{{#if this.scriptFields}} <ComboBox
<section class="fields-section form-section edit"> @value={{@controller.automationForm.script}}
@content={{@controller.model.scriptables}}
@onChange={{@controller.onChangeScript}}
@options={{hash filterable=true}}
class="scriptables"
/>
</div>
</div>
</section>
<section class="trigger-section form-section edit">
<h2 class="title"> <h2 class="title">
{{i18n {{i18n
"discourse_automation.edit_automation.fields_section.title" "discourse_automation.edit_automation.trigger_section.title"
}} }}
</h2> </h2>
{{#if this.model.automation.script.with_trigger_doc}} <div class="control-group">
<div class="alert alert-info"> {{#if @controller.model.automation.script.forced_triggerable}}
<p>{{this.model.automation.script.with_trigger_doc}}</p> <div class="alert alert-warning">
{{i18n
"discourse_automation.edit_automation.trigger_section.forced"
}}
</div>
{{/if}}
<label class="control-label">
{{i18n "discourse_automation.models.trigger.name.label"}}
</label>
<div class="controls">
<ComboBox
@value={{@controller.automationForm.trigger}}
@content={{@controller.model.triggerables}}
@onChange={{@controller.onChangeTrigger}}
@options={{hash
filterable=true
none="discourse_automation.select_trigger"
disabled=@controller.model.automation.script.forced_triggerable
}}
class="triggerables"
/>
</div>
</div>
{{#if @controller.automationForm.trigger}}
{{#if @controller.model.automation.trigger.doc}}
<div class="alert alert-info">
<p>{{@controller.model.automation.trigger.doc}}</p>
</div>
{{/if}}
{{#if
(and
@controller.model.automation.enabled
@controller.model.automation.trigger.settings.manual_trigger
)
}}
<div class="alert alert-info next-trigger">
{{#if @controller.nextPendingAutomationAtFormatted}}
<p>
{{i18n
"discourse_automation.edit_automation.trigger_section.next_pending_automation"
date=@controller.nextPendingAutomationAtFormatted
}}
</p>
{{/if}}
<DButton
@label="discourse_automation.edit_automation.trigger_section.trigger_now"
@isLoading={{@controller.isTriggeringAutomation}}
@action={{fn
@controller.onManualAutomationTrigger
@controller.model.automation.id
}}
class="btn-primary trigger-now-btn"
/>
</div>
{{/if}}
{{#each @controller.triggerFields as |field|}}
<AutomationField
@automation={{@controller.automation}}
@field={{field}}
@saveAutomation={{fn
@controller.saveAutomation
@controller.automation
}}
/>
{{/each}}
{{/if}}
</section>
{{#if @controller.automationForm.trigger}}
{{#if @controller.scriptFields}}
<section class="fields-section form-section edit">
<h2 class="title">
{{i18n
"discourse_automation.edit_automation.fields_section.title"
}}
</h2>
{{#if @controller.model.automation.script.with_trigger_doc}}
<div class="alert alert-info">
<p
>{{@controller.model.automation.script.with_trigger_doc}}</p>
</div>
{{/if}}
<div class="control-group">
{{#each @controller.scriptFields as |field|}}
<AutomationField
@automation={{@controller.automation}}
@field={{field}}
@saveAutomation={{fn
@controller.saveAutomation
@controller.automation
}}
/>
{{/each}}
</div>
</section>
{{/if}}
{{#if @controller.automationForm.trigger}}
<div
class="control-group automation-enabled alert
{{if
@controller.automationForm.enabled
'alert-info'
'alert-warning'
}}"
>
<span>{{i18n
"discourse_automation.models.automation.enabled.label"
}}</span>
<Input
@type="checkbox"
@checked={{@controller.automationForm.enabled}}
{{on
"click"
(action
(mut @controller.automationForm.enabled)
value="target.checked"
)
}}
/>
</div> </div>
{{/if}} {{/if}}
<div class="control-group"> <div class="control-group">
{{#each this.scriptFields as |field|}} <DButton
<AutomationField @isLoading={{@controller.isUpdatingAutomation}}
@automation={{this.automation}} @label="discourse_automation.update"
@field={{field}} @type="submit"
@saveAutomation={{fn this.saveAutomation this.automation}} @action={{fn
/> @controller.saveAutomation
{{/each}} @controller.automation
true
}}
class="btn-primary update-automation"
/>
</div> </div>
</section> {{/if}}
{{/if}} </form>
</:content>
{{#if this.automationForm.trigger}} </AdminConfigAreaCard>
<div </div>
class="control-group automation-enabled alert </template>
{{if this.automationForm.enabled 'alert-info' 'alert-warning'}}" );
>
<span>{{i18n
"discourse_automation.models.automation.enabled.label"
}}</span>
<Input
@type="checkbox"
@checked={{this.automationForm.enabled}}
{{on
"click"
(action
(mut this.automationForm.enabled) value="target.checked"
)
}}
/>
</div>
{{/if}}
<div class="control-group">
<DButton
@isLoading={{this.isUpdatingAutomation}}
@label="discourse_automation.update"
@type="submit"
@action={{fn this.saveAutomation this.automation true}}
class="btn-primary update-automation"
/>
</div>
{{/if}}
</form>
</:content>
</AdminConfigAreaCard>
</div>

View File

@ -1 +1,6 @@
<AutomationList @model={{this.model}} /> import RouteTemplate from "ember-route-template";
import AutomationList from "discourse/plugins/automation/admin/components/automation-list";
export default RouteTemplate(
<template><AutomationList @model={{@controller.model}} /></template>
);

View File

@ -1,27 +1,41 @@
<div class="admin-detail discourse-automation-new discourse-automation-form"> import { fn } from "@ember/helper";
<BackButton import { on } from "@ember/modifier";
@label="discourse_automation.back" import RouteTemplate from "ember-route-template";
@route="adminPlugins.show.automation.index" import BackButton from "discourse/components/back-button";
class="discourse-automation-back" import { i18n } from "discourse-i18n";
/> import AdminConfigAreaCard from "admin/components/admin-config-area-card";
<AdminConfigAreaCard @heading="discourse_automation.select_script"> import AdminSectionLandingItem from "admin/components/admin-section-landing-item";
<:content>
<input
type="text"
placeholder={{i18n "discourse_automation.filter_placeholder"}}
{{on "input" this.updateFilterText}}
class="admin-section-landing__header-filter"
/>
<div class="admin-section-landing__wrapper"> export default RouteTemplate(
{{#each this.scriptableContent as |script|}} <template>
<AdminSectionLandingItem <div
{{on "click" (fn this.selectScriptToEdit script)}} class="admin-detail discourse-automation-new discourse-automation-form"
@titleLabelTranslated={{script.name}} >
@descriptionLabelTranslated={{script.description}} <BackButton
@label="discourse_automation.back"
@route="adminPlugins.show.automation.index"
class="discourse-automation-back"
/>
<AdminConfigAreaCard @heading="discourse_automation.select_script">
<:content>
<input
type="text"
placeholder={{i18n "discourse_automation.filter_placeholder"}}
{{on "input" @controller.updateFilterText}}
class="admin-section-landing__header-filter"
/> />
{{/each}}
</div> <div class="admin-section-landing__wrapper">
</:content> {{#each @controller.scriptableContent as |script|}}
</AdminConfigAreaCard> <AdminSectionLandingItem
</div> {{on "click" (fn @controller.selectScriptToEdit script)}}
@titleLabelTranslated={{script.name}}
@descriptionLabelTranslated={{script.description}}
/>
{{/each}}
</div>
</:content>
</AdminConfigAreaCard>
</div>
</template>
);

View File

@ -1,8 +1,8 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { click, render } from "@ember/test-helpers"; import { click, render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-boolean-field", function (hooks) { module("Integration | Component | da-boolean-field", function (hooks) {
@ -13,10 +13,17 @@ module("Integration | Component | da-boolean-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field(); this.field = new AutomationFabricators(getOwner(this)).field();
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await click("input"); await click("input");

View File

@ -1,9 +1,9 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-categories-field", function (hooks) { module("Integration | Component | da-categories-field", function (hooks) {
@ -14,12 +14,19 @@ module("Integration | Component | da-categories-field", function (hooks) {
}); });
test("sets values", async function (assert) { test("sets values", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "categories", component: "categories",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,9 +1,9 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-category-field", function (hooks) { module("Integration | Component | da-category-field", function (hooks) {
@ -14,12 +14,19 @@ module("Integration | Component | da-category-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "category", component: "category",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,9 +1,9 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import notificationsTracking from "discourse/tests/helpers/notifications-tracking-helper"; import notificationsTracking from "discourse/tests/helpers/notifications-tracking-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module( module(
@ -16,12 +16,19 @@ module(
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "category_notification_level", component: "category_notification_level",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await notificationsTracking().selectLevelId(2); await notificationsTracking().selectLevelId(2);

View File

@ -1,9 +1,9 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-choices-field", function (hooks) { module("Integration | Component | da-choices-field", function (hooks) {
@ -14,13 +14,20 @@ module("Integration | Component | da-choices-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "choices", component: "choices",
extra: { content: [{ name: "One", id: 1 }] }, extra: { content: [{ name: "One", id: 1 }] },
}); });
await render( await render(
hbs` <AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,10 +1,10 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender"; import pretender, { response } from "discourse/tests/helpers/create-pretender";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-custom-field", function (hooks) { module("Integration | Component | da-custom-field", function (hooks) {
@ -34,12 +34,19 @@ module("Integration | Component | da-custom-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "custom_field", component: "custom_field",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,8 +1,8 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { fillIn, render } from "@ember/test-helpers"; import { fillIn, render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-date-time-field", function (hooks) { module("Integration | Component | da-date-time-field", function (hooks) {
@ -13,12 +13,19 @@ module("Integration | Component | da-date-time-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "date_time", component: "date_time",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await fillIn("input", "2023-10-03T12:34"); await fillIn("input", "2023-10-03T12:34");

View File

@ -1,13 +1,15 @@
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import DaEmailGroupUserField from "discourse/plugins/automation/admin/components/fields/da-email-group-user-field";
module("Integration | Component | email-group-user-field", function (hooks) { module("Integration | Component | email-group-user-field", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("Email group user field uses email-group-user-chooser component", async function (assert) { test("Email group user field uses email-group-user-chooser component", async function (assert) {
const template = hbs` <Fields::DaEmailGroupUserField @label="a label" />`; const template = <template>
<DaEmailGroupUserField @label="a label" />
</template>;
await render(template); await render(template);

View File

@ -1,10 +1,10 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender"; import pretender, { response } from "discourse/tests/helpers/create-pretender";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-group-field", function (hooks) { module("Integration | Component | da-group-field", function (hooks) {
@ -27,12 +27,19 @@ module("Integration | Component | da-group-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "group", component: "group",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,10 +1,10 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender"; import pretender, { response } from "discourse/tests/helpers/create-pretender";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-groups-field", function (hooks) { module("Integration | Component | da-groups-field", function (hooks) {
@ -34,12 +34,19 @@ module("Integration | Component | da-groups-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "groups", component: "groups",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();
@ -49,13 +56,20 @@ module("Integration | Component | da-groups-field", function (hooks) {
}); });
test("supports a maxmimum value", async function (assert) { test("supports a maxmimum value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "groups", component: "groups",
extra: { maximum: 1 }, extra: { maximum: 1 },
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,8 +1,8 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { fillIn, render } from "@ember/test-helpers"; import { fillIn, render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-message-field", function (hooks) { module("Integration | Component | da-message-field", function (hooks) {
@ -13,12 +13,19 @@ module("Integration | Component | da-message-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "message", component: "message",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await fillIn("textarea", "Hello World"); await fillIn("textarea", "Hello World");
@ -26,13 +33,20 @@ module("Integration | Component | da-message-field", function (hooks) {
}); });
test("render placeholders", async function (assert) { test("render placeholders", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "message", component: "message",
}); });
this.automation.placeholders = ["foo", "bar"]; this.automation.placeholders = ["foo", "bar"];
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
assert.dom(".placeholders-list").hasText("foo bar"); assert.dom(".placeholders-list").hasText("foo bar");

View File

@ -1,8 +1,8 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { click, fillIn, render } from "@ember/test-helpers"; import { click, fillIn, render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-pms-field", function (hooks) { module("Integration | Component | da-pms-field", function (hooks) {
@ -13,12 +13,19 @@ module("Integration | Component | da-pms-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "pms", component: "pms",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await click(".insert-pm"); await click(".insert-pm");

View File

@ -1,8 +1,8 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { fillIn, render } from "@ember/test-helpers"; import { fillIn, render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-post-field", function (hooks) { module("Integration | Component | da-post-field", function (hooks) {
@ -13,12 +13,19 @@ module("Integration | Component | da-post-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "post", component: "post",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await fillIn("textarea", "Hello World"); await fillIn("textarea", "Hello World");

View File

@ -1,9 +1,9 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-tags-field", function (hooks) { module("Integration | Component | da-tags-field", function (hooks) {
@ -14,12 +14,19 @@ module("Integration | Component | da-tags-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "tags", component: "tags",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,8 +1,8 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { fillIn, render } from "@ember/test-helpers"; import { fillIn, render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-text-field", function (hooks) { module("Integration | Component | da-text-field", function (hooks) {
@ -13,12 +13,19 @@ module("Integration | Component | da-text-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "text", component: "text",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await fillIn("input", "Hello World"); await fillIn("input", "Hello World");

View File

@ -1,9 +1,9 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-text-list-field", function (hooks) { module("Integration | Component | da-text-list-field", function (hooks) {
@ -14,12 +14,19 @@ module("Integration | Component | da-text-list-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "text_list", component: "text_list",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();
await selectKit().fillInFilter("test"); await selectKit().fillInFilter("test");

View File

@ -1,9 +1,9 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-trust-levels-field", function (hooks) { module("Integration | Component | da-trust-levels-field", function (hooks) {
@ -14,12 +14,19 @@ module("Integration | Component | da-trust-levels-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "trust-levels", component: "trust-levels",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,10 +1,10 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender"; import pretender, { response } from "discourse/tests/helpers/create-pretender";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-user-field", function (hooks) { module("Integration | Component | da-user-field", function (hooks) {
@ -27,12 +27,19 @@ module("Integration | Component | da-user-field", function (hooks) {
}); });
test("set value", async function (assert) { test("set value", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "user", component: "user",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -1,10 +1,10 @@
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import { hbs } from "ember-cli-htmlbars";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender"; import pretender, { response } from "discourse/tests/helpers/create-pretender";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import AutomationField from "discourse/plugins/automation/admin/components/automation-field";
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators"; import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
module("Integration | Component | da-users-field", function (hooks) { module("Integration | Component | da-users-field", function (hooks) {
@ -32,12 +32,19 @@ module("Integration | Component | da-users-field", function (hooks) {
}); });
test("sets values", async function (assert) { test("sets values", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "users", component: "users",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();
@ -50,12 +57,19 @@ module("Integration | Component | da-users-field", function (hooks) {
}); });
test("allows emails", async function (assert) { test("allows emails", async function (assert) {
const self = this;
this.field = new AutomationFabricators(getOwner(this)).field({ this.field = new AutomationFabricators(getOwner(this)).field({
component: "users", component: "users",
}); });
await render( await render(
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />` <template>
<AutomationField
@automation={{self.automation}}
@field={{self.field}}
/>
</template>
); );
await selectKit().expand(); await selectKit().expand();

View File

@ -3,8 +3,24 @@ import { cached } from "@glimmer/tracking";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatComposerMessageDetails from "discourse/plugins/chat/discourse/components/chat-composer-message-details";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const StyleguideComponent = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/component"
);
const Controls = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls"
);
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatComposerMessageDetails extends Component { export default class ChatStyleguideChatComposerMessageDetails extends Component {
@service site; @service site;
@service session; @service session;
@ -28,20 +44,22 @@ export default class ChatStyleguideChatComposerMessageDetails extends Component
this.message.inReplyTo = null; this.message.inReplyTo = null;
} }
} }
<template>
<StyleguideExample @title="<ChatComposerMessageDetails>">
<StyleguideComponent>
<ChatComposerMessageDetails @message={{this.message}} />
</StyleguideComponent>
<Controls>
<Row @name="Mode">
{{#if this.message.editing}}
<DButton @action={{this.toggleMode}} @translatedLabel="Reply" />
{{else}}
<DButton @action={{this.toggleMode}} @translatedLabel="Editing" />
{{/if}}
</Row>
</Controls>
</StyleguideExample>
</template>
} }
<StyleguideExample @title="<ChatComposerMessageDetails>">
<Styleguide::Component>
<ChatComposerMessageDetails @message={{this.message}} />
</Styleguide::Component>
<Styleguide::Controls>
<Styleguide::Controls::Row @name="Mode">
{{#if this.message.editing}}
<DButton @action={{this.toggleMode}} @translatedLabel="Reply" />
{{else}}
<DButton @action={{this.toggleMode}} @translatedLabel="Editing" />
{{/if}}
</Styleguide::Controls::Row>
</Styleguide::Controls>
</StyleguideExample>

View File

@ -1,10 +1,27 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import { optionalRequire } from "discourse/lib/utilities";
import Channel from "discourse/plugins/chat/discourse/components/chat/composer/channel";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel"; import { CHANNEL_STATUSES } from "discourse/plugins/chat/discourse/models/chat-channel";
const StyleguideComponent = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/component"
);
const Controls = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls"
);
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatComposer extends Component { export default class ChatStyleguideChatComposer extends Component {
@service chatChannelComposer; @service chatChannelComposer;
@service chatChannelPane; @service chatChannelPane;
@ -29,28 +46,30 @@ export default class ChatStyleguideChatComposer extends Component {
onSendMessage() { onSendMessage() {
this.chatChannelComposer.reset(); this.chatChannelComposer.reset();
} }
<template>
<StyleguideExample @title="<ChatComposer>">
<StyleguideComponent>
<Channel
@channel={{this.channel}}
@onSendMessage={{this.onSendMessage}}
/>
</StyleguideComponent>
<Controls>
<Row @name="Disabled">
<DToggleSwitch
@state={{this.channel.isReadOnly}}
{{on "click" this.toggleDisabled}}
/>
</Row>
<Row @name="Sending">
<DToggleSwitch
@state={{this.chatChannelPane.sending}}
{{on "click" this.toggleSending}}
/>
</Row>
</Controls>
</StyleguideExample>
</template>
} }
<StyleguideExample @title="<ChatComposer>">
<Styleguide::Component>
<Chat::Composer::Channel
@channel={{this.channel}}
@onSendMessage={{this.onSendMessage}}
/>
</Styleguide::Component>
<Styleguide::Controls>
<Styleguide::Controls::Row @name="Disabled">
<DToggleSwitch
@state={{this.channel.isReadOnly}}
{{on "click" this.toggleDisabled}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="Sending">
<DToggleSwitch
@state={{this.chatChannelPane.sending}}
{{on "click" this.toggleSending}}
/>
</Styleguide::Controls::Row>
</Styleguide::Controls>
</StyleguideExample>

View File

@ -1,6 +1,11 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import { optionalRequire } from "discourse/lib/utilities";
import ComboBox from "select-kit/components/combo-box";
import Icon from "discourse/plugins/chat/discourse/components/chat/header/icon";
import { import {
HEADER_INDICATOR_PREFERENCE_ALL_NEW, HEADER_INDICATOR_PREFERENCE_ALL_NEW,
HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS, HEADER_INDICATOR_PREFERENCE_DM_AND_MENTIONS,
@ -8,6 +13,19 @@ import {
HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS, HEADER_INDICATOR_PREFERENCE_ONLY_MENTIONS,
} from "discourse/plugins/chat/discourse/controllers/preferences-chat"; } from "discourse/plugins/chat/discourse/controllers/preferences-chat";
const StyleguideComponent = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/component"
);
const Controls = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls"
);
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatHeaderIcon extends Component { export default class ChatStyleguideChatHeaderIcon extends Component {
@tracked isActive = false; @tracked isActive = false;
@tracked currentUserInDnD = false; @tracked currentUserInDnD = false;
@ -48,64 +66,66 @@ export default class ChatStyleguideChatHeaderIcon extends Component {
updateIndicatorPreference(value) { updateIndicatorPreference(value) {
this.indicatorPreference = value; this.indicatorPreference = value;
} }
}
<StyleguideExample @title="<Chat::Header::Icon>"> <template>
<Styleguide::Component> <StyleguideExample @title="<Chat::Header::Icon>">
<header <StyleguideComponent>
class="d-header" <header
style="display: flex; align-items: center; justify-content: center;" class="d-header"
> style="display: flex; align-items: center; justify-content: center;"
<ul class="d-header-icons"> >
<li class="header-dropdown-toggle chat-header-icon"> <ul class="d-header-icons">
<Chat::Header::Icon <li class="header-dropdown-toggle chat-header-icon">
@isActive={{this.isActive}} <Icon
@currentUserInDnD={{this.currentUserInDnD}} @isActive={{this.isActive}}
@unreadCount={{this.unreadCount}} @currentUserInDnD={{this.currentUserInDnD}}
@urgentCount={{this.urgentCount}} @unreadCount={{this.unreadCount}}
@indicatorPreference={{this.indicatorPreference}} @urgentCount={{this.urgentCount}}
@indicatorPreference={{this.indicatorPreference}}
/>
</li>
</ul>
</header>
</StyleguideComponent>
<Controls>
<Row @name="isActive">
<DToggleSwitch
@state={{this.isActive}}
{{on "click" this.toggleIsActive}}
/> />
</li> </Row>
</ul> <Row @name="currentUserInDnD">
</header> <DToggleSwitch
</Styleguide::Component> @state={{this.currentUserInDnD}}
{{on "click" this.toggleCurrentUserInDnD}}
/>
</Row>
</Controls>
<Row @name="Unread count">
<input
type="number"
{{on "input" this.updateUnreadCount}}
value={{this.unreadCount}}
/>
</Row>
<Row @name="Urgent count">
<input
type="number"
{{on "input" this.updateUrgentCount}}
value={{this.urgentCount}}
/>
</Row>
<Row @name="Indicator preference">
<ComboBox
@value={{this.indicatorPreference}}
@content={{this.indicatorPreferences}}
@onChange={{this.updateIndicatorPreference}}
@valueProperty={{null}}
@nameProperty={{null}}
/>
<Styleguide::Controls> </Row>
<Styleguide::Controls::Row @name="isActive"> </StyleguideExample>
<DToggleSwitch </template>
@state={{this.isActive}} }
{{on "click" this.toggleIsActive}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="currentUserInDnD">
<DToggleSwitch
@state={{this.currentUserInDnD}}
{{on "click" this.toggleCurrentUserInDnD}}
/>
</Styleguide::Controls::Row>
</Styleguide::Controls>
<Styleguide::Controls::Row @name="Unread count">
<input
type="number"
{{on "input" this.updateUnreadCount}}
value={{this.unreadCount}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="Urgent count">
<input
type="number"
{{on "input" this.updateUrgentCount}}
value={{this.urgentCount}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="Indicator preference">
<ComboBox
@value={{this.indicatorPreference}}
@content={{this.indicatorPreferences}}
@onChange={{this.updateIndicatorPreference}}
@valueProperty={{null}}
@nameProperty={{null}}
/>
</Styleguide::Controls::Row>
</StyleguideExample>

View File

@ -1,10 +1,28 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { not } from "truth-helpers";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import { optionalRequire } from "discourse/lib/utilities";
import ChatMessage from "discourse/plugins/chat/discourse/components/chat-message";
import ChatMessagesManager from "discourse/plugins/chat/discourse/lib/chat-messages-manager"; import ChatMessagesManager from "discourse/plugins/chat/discourse/lib/chat-messages-manager";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const StyleguideComponent = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/component"
);
const Controls = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls"
);
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatMessage extends Component { export default class ChatStyleguideChatMessage extends Component {
@service currentUser; @service currentUser;
@ -92,48 +110,50 @@ export default class ChatStyleguideChatMessage extends Component {
]; ];
} }
} }
<template>
<StyleguideExample @title="<ChatMessage>">
<StyleguideComponent>
<ChatMessage @message={{this.message}} @context="channel" />
</StyleguideComponent>
<Controls>
<Row @name="Deleted">
<DToggleSwitch
@state={{not (not this.message.deletedAt)}}
{{on "click" this.toggleDeleted}}
/>
</Row>
<Row @name="Bookmark">
<DToggleSwitch
@state={{not (not this.message.bookmark)}}
{{on "click" this.toggleBookmarked}}
/>
</Row>
<Row @name="Thread">
<DToggleSwitch
@state={{not (not this.message.thread)}}
{{on "click" this.toggleThread}}
/>
</Row>
<Row @name="Reactions">
<DToggleSwitch
@state={{not (not this.message.reactions)}}
{{on "click" this.toggleReaction}}
/>
</Row>
<Row @name="Upload">
<DToggleSwitch
@state={{not (not this.message.uploads)}}
{{on "click" this.toggleUpload}}
/>
</Row>
<Row @name="Message">
<textarea
{{on "input" this.updateMessage}}
>{{this.message.message}}</textarea>
</Row>
</Controls>
</StyleguideExample>
</template>
} }
<StyleguideExample @title="<ChatMessage>">
<Styleguide::Component>
<ChatMessage @message={{this.message}} @context="channel" />
</Styleguide::Component>
<Styleguide::Controls>
<Styleguide::Controls::Row @name="Deleted">
<DToggleSwitch
@state={{not (not this.message.deletedAt)}}
{{on "click" this.toggleDeleted}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="Bookmark">
<DToggleSwitch
@state={{not (not this.message.bookmark)}}
{{on "click" this.toggleBookmarked}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="Thread">
<DToggleSwitch
@state={{not (not this.message.thread)}}
{{on "click" this.toggleThread}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="Reactions">
<DToggleSwitch
@state={{not (not this.message.reactions)}}
{{on "click" this.toggleReaction}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="Upload">
<DToggleSwitch
@state={{not (not this.message.uploads)}}
{{on "click" this.toggleUpload}}
/>
</Styleguide::Controls::Row>
<Styleguide::Controls::Row @name="Message">
<textarea
{{on "input" this.updateMessage}}
>{{this.message.message}}</textarea>
</Styleguide::Controls::Row>
</Styleguide::Controls>
</StyleguideExample>

View File

@ -2,9 +2,18 @@ import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalArchiveChannel from "discourse/plugins/chat/discourse/components/chat/modal/archive-channel"; import ChatModalArchiveChannel from "discourse/plugins/chat/discourse/components/chat/modal/archive-channel";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalArchiveChannel extends Component { export default class ChatStyleguideChatModalArchiveChannel extends Component {
@service modal; @service modal;
@ -18,10 +27,12 @@ export default class ChatStyleguideChatModalArchiveChannel extends Component {
}, },
}); });
} }
}
<StyleguideExample @title="<Chat::Modal::ArchiveChannel>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::ArchiveChannel>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -1,8 +1,17 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalCreateChannel from "discourse/plugins/chat/discourse/components/chat/modal/create-channel"; import ChatModalCreateChannel from "discourse/plugins/chat/discourse/components/chat/modal/create-channel";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalCreateChannel extends Component { export default class ChatStyleguideChatModalCreateChannel extends Component {
@service modal; @service modal;
@ -10,10 +19,12 @@ export default class ChatStyleguideChatModalCreateChannel extends Component {
openModal() { openModal() {
return this.modal.show(ChatModalCreateChannel); return this.modal.show(ChatModalCreateChannel);
} }
}
<StyleguideExample @title="<Chat::Modal::CreateChannel>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::CreateChannel>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -2,9 +2,18 @@ import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalDeleteChannel from "discourse/plugins/chat/discourse/components/chat/modal/delete-channel"; import ChatModalDeleteChannel from "discourse/plugins/chat/discourse/components/chat/modal/delete-channel";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalDeleteChannel extends Component { export default class ChatStyleguideChatModalDeleteChannel extends Component {
@service modal; @service modal;
@ -16,10 +25,12 @@ export default class ChatStyleguideChatModalDeleteChannel extends Component {
model: { channel: this.channel }, model: { channel: this.channel },
}); });
} }
}
<StyleguideExample @title="<Chat::Modal::DeleteChannel>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::DeleteChannel>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -2,9 +2,18 @@ import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalEditChannelDescription from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-description"; import ChatModalEditChannelDescription from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-description";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalEditChannelDescription extends Component { export default class ChatStyleguideChatModalEditChannelDescription extends Component {
@service modal; @service modal;
@ -16,10 +25,12 @@ export default class ChatStyleguideChatModalEditChannelDescription extends Compo
model: this.channel, model: this.channel,
}); });
} }
}
<StyleguideExample @title="<Chat::Modal::EditChannelDescription>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::EditChannelDescription>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -2,9 +2,18 @@ import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalEditChannelName from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-name"; import ChatModalEditChannelName from "discourse/plugins/chat/discourse/components/chat/modal/edit-channel-name";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalEditChannelName extends Component { export default class ChatStyleguideChatModalEditChannelName extends Component {
@service modal; @service modal;
@ -16,10 +25,12 @@ export default class ChatStyleguideChatModalEditChannelName extends Component {
model: this.channel, model: this.channel,
}); });
} }
}
<StyleguideExample @title="<Chat::Modal::EditChannelName>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::EditChannelName>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -2,9 +2,18 @@ import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalMoveMessageToChannel from "discourse/plugins/chat/discourse/components/chat/modal/move-message-to-channel"; import ChatModalMoveMessageToChannel from "discourse/plugins/chat/discourse/components/chat/modal/move-message-to-channel";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalMoveMessageToChannel extends Component { export default class ChatStyleguideChatModalMoveMessageToChannel extends Component {
@service modal; @service modal;
@ -26,10 +35,12 @@ export default class ChatStyleguideChatModalMoveMessageToChannel extends Compone
}, },
}); });
} }
}
<StyleguideExample @title="<Chat::Modal::MoveMessageToChannel>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::MoveMessageToChannel>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -1,8 +1,17 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message"; import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalNewMessage extends Component { export default class ChatStyleguideChatModalNewMessage extends Component {
@service modal; @service modal;
@ -10,10 +19,12 @@ export default class ChatStyleguideChatModalNewMessage extends Component {
openModal() { openModal() {
return this.modal.show(ChatModalNewMessage); return this.modal.show(ChatModalNewMessage);
} }
}
<StyleguideExample @title="<Chat::Modal::NewMessage>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::NewMessage>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -2,9 +2,18 @@ import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalThreadSettings from "discourse/plugins/chat/discourse/components/chat/modal/thread-settings"; import ChatModalThreadSettings from "discourse/plugins/chat/discourse/components/chat/modal/thread-settings";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalThreadSettings extends Component { export default class ChatStyleguideChatModalThreadSettings extends Component {
@service modal; @service modal;
@ -14,10 +23,12 @@ export default class ChatStyleguideChatModalThreadSettings extends Component {
model: new ChatFabricators(getOwner(this)).thread(), model: new ChatFabricators(getOwner(this)).thread(),
}); });
} }
}
<StyleguideExample @title="<Chat::Modal::ThreadSettings>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::ThreadSettings>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -2,9 +2,18 @@ import Component from "@glimmer/component";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { service } from "@ember/service"; import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import { optionalRequire } from "discourse/lib/utilities";
import ChatModalToggleChannelStatus from "discourse/plugins/chat/discourse/components/chat/modal/toggle-channel-status"; import ChatModalToggleChannelStatus from "discourse/plugins/chat/discourse/components/chat/modal/toggle-channel-status";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const Row = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/controls/row"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatModalToggleChannelStatus extends Component { export default class ChatStyleguideChatModalToggleChannelStatus extends Component {
@service modal; @service modal;
@ -14,10 +23,12 @@ export default class ChatStyleguideChatModalToggleChannelStatus extends Componen
model: new ChatFabricators(getOwner(this)).channel(), model: new ChatFabricators(getOwner(this)).channel(),
}); });
} }
}
<StyleguideExample @title="<Chat::Modal::ToggleChannelStatus>"> <template>
<Styleguide::Controls::Row> <StyleguideExample @title="<Chat::Modal::ToggleChannelStatus>">
<DButton @translatedLabel="Open modal" @action={{this.openModal}} /> <Row>
</Styleguide::Controls::Row> <DButton @translatedLabel="Open modal" @action={{this.openModal}} />
</StyleguideExample> </Row>
</StyleguideExample>
</template>
}

View File

@ -3,8 +3,17 @@ import { tracked } from "@glimmer/tracking";
import { getOwner } from "@ember/owner"; import { getOwner } from "@ember/owner";
import { next } from "@ember/runloop"; import { next } from "@ember/runloop";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { optionalRequire } from "discourse/lib/utilities";
import Item from "discourse/plugins/chat/discourse/components/chat/thread-list/item";
import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators"; import ChatFabricators from "discourse/plugins/chat/discourse/lib/fabricators";
const StyleguideComponent = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide/component"
);
const StyleguideExample = optionalRequire(
"discourse/plugins/styleguide/discourse/components/styleguide-example"
);
export default class ChatStyleguideChatThreadListItem extends Component { export default class ChatStyleguideChatThreadListItem extends Component {
@service currentUser; @service currentUser;
@ -17,12 +26,14 @@ export default class ChatStyleguideChatThreadListItem extends Component {
this.thread = new ChatFabricators(getOwner(this)).thread(); this.thread = new ChatFabricators(getOwner(this)).thread();
}); });
} }
}
<StyleguideExample @title="<Chat::ThreadList::Item>"> <template>
<Styleguide::Component> <StyleguideExample @title="<Chat::ThreadList::Item>">
{{#if this.thread}} <StyleguideComponent>
<Chat::ThreadList::Item @thread={{this.thread}} /> {{#if this.thread}}
{{/if}} <Item @thread={{this.thread}} />
</Styleguide::Component> {{/if}}
</StyleguideExample> </StyleguideComponent>
</StyleguideExample>
</template>
}

View File

@ -1,14 +1,25 @@
import Component from "@ember/component"; import Component from "@ember/component";
import { fn, hash } from "@ember/helper";
import { on } from "@ember/modifier";
import EmberObject, { action } from "@ember/object"; import EmberObject, { action } from "@ember/object";
import { notEmpty } from "@ember/object/computed"; import { notEmpty } from "@ember/object/computed";
import { schedule } from "@ember/runloop"; import { schedule } from "@ember/runloop";
import { observes } from "@ember-decorators/object"; import { observes } from "@ember-decorators/object";
import CalendarDateTimeInput from "discourse/components/calendar-date-time-input";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import TextField from "discourse/components/text-field";
import icon from "discourse/helpers/d-icon";
import htmlSafe from "discourse/helpers/html-safe";
import { propertyNotEqual } from "discourse/lib/computed"; import { propertyNotEqual } from "discourse/lib/computed";
import computed, { debounce } from "discourse/lib/decorators"; import computed, { debounce } from "discourse/lib/decorators";
import { INPUT_DELAY } from "discourse/lib/environment"; import { INPUT_DELAY } from "discourse/lib/environment";
import { applyLocalDates } from "discourse/lib/local-dates"; import { applyLocalDates } from "discourse/lib/local-dates";
import { cook } from "discourse/lib/text"; import { cook } from "discourse/lib/text";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import MultiSelect from "select-kit/components/multi-select";
import TimezoneInput from "select-kit/components/timezone-input";
import generateDateMarkup from "discourse/plugins/discourse-local-dates/lib/local-date-markup-generator"; import generateDateMarkup from "discourse/plugins/discourse-local-dates/lib/local-date-markup-generator";
export default class LocalDatesCreate extends Component { export default class LocalDatesCreate extends Component {
@ -371,205 +382,213 @@ export default class LocalDatesCreate extends Component {
cancel() { cancel() {
this.closeModal(); this.closeModal();
} }
}
<DModal <template>
@title={{i18n "discourse_local_dates.title"}} <DModal
@closeModal={{@closeModal}} @title={{i18n "discourse_local_dates.title"}}
class="discourse-local-dates-create-modal -large" @closeModal={{@closeModal}}
> class="discourse-local-dates-create-modal -large"
<:body> >
<div class="form"> <:body>
{{#if this.isValid}} <div class="form">
{{#if this.timezoneIsDifferentFromUserTimezone}} {{#if this.isValid}}
<div class="preview alert alert-info"> {{#if this.timezoneIsDifferentFromUserTimezone}}
{{i18n "discourse_local_dates.create.form.current_timezone"}} <div class="preview alert alert-info">
<b>{{this.formattedCurrentUserTimezone}}</b>{{this.currentPreview}} {{i18n "discourse_local_dates.create.form.current_timezone"}}
</div> <b
{{/if}} >{{this.formattedCurrentUserTimezone}}</b>{{this.currentPreview}}
{{else}} </div>
<div class="validation-error alert alert-error"> {{/if}}
{{i18n "discourse_local_dates.create.form.invalid_date"}} {{else}}
</div> <div class="validation-error alert alert-error">
{{/if}} {{i18n "discourse_local_dates.create.form.invalid_date"}}
</div>
{{/if}}
{{this.computeDate}} {{this.computeDate}}
<div class="date-time-configuration"> <div class="date-time-configuration">
<div class="inputs-panel"> <div class="inputs-panel">
<div <div
class="date-time-control from class="date-time-control from
{{if this.fromSelected 'is-selected'}} {{if this.fromSelected 'is-selected'}}
{{if this.fromFilled 'is-filled'}}" {{if this.fromFilled 'is-filled'}}"
> >
{{d-icon "calendar-days"}} {{icon "calendar-days"}}
<DButton <DButton
@action={{this.focusFrom}} @action={{this.focusFrom}}
@translatedLabel={{this.formattedFrom}} @translatedLabel={{this.formattedFrom}}
id="from-date-time" id="from-date-time"
class="date-time" class="date-time"
autofocus autofocus
/> />
</div> </div>
<div <div
class="date-time-control to class="date-time-control to
{{if this.toSelected 'is-selected'}} {{if this.toSelected 'is-selected'}}
{{if this.toFilled 'is-filled'}}" {{if this.toFilled 'is-filled'}}"
> >
{{d-icon "calendar-days"}} {{icon "calendar-days"}}
<DButton <DButton
@action={{this.focusTo}} @action={{this.focusTo}}
@translatedLabel={{this.formattedTo}} @translatedLabel={{this.formattedTo}}
class="date-time" class="date-time"
/> />
{{#if this.toFilled}} {{#if this.toFilled}}
<DButton <DButton
@action={{this.eraseToDateTime}} @action={{this.eraseToDateTime}}
@icon="xmark" @icon="xmark"
class="delete-to-date" class="delete-to-date"
/>
{{/if}}
</div>
{{#if this.site.desktopView}}
<TimezoneInput
@options={{hash icon="globe"}}
@value={{this.timezone}}
@onChange={{fn (mut this.timezone)}}
/>
{{/if}}
</div>
<div class="picker-panel">
<CalendarDateTimeInput
@datePickerId="local-date-create-form"
@date={{this.selectedDate}}
@time={{this.selectedTime}}
@minDate={{this.minDate}}
@timeFormat={{this.timeFormat}}
@dateFormat={{this.dateFormat}}
@onChangeDate={{this.changeSelectedDate}}
@onChangeTime={{this.changeSelectedTime}}
/>
</div>
{{#if this.site.mobileView}}
<TimezoneInput
@value={{this.timezone}}
@options={{hash icon="globe"}}
@onChange={{fn (mut this.timezone)}}
/> />
{{/if}} {{/if}}
</div> </div>
{{#if this.site.desktopView}} {{#if this.advancedMode}}
<TimezoneInput <div class="advanced-options">
@options={{hash icon="globe"}} {{#unless this.isRange}}
@value={{this.timezone}} <div class="control-group recurrence">
@onChange={{fn (mut this.timezone)}} <label class="control-label">
/> {{i18n "discourse_local_dates.create.form.recurring_title"}}
{{/if}} </label>
</div> <p>{{htmlSafe
(i18n
"discourse_local_dates.create.form.recurring_description"
)
}}</p>
<div class="controls">
<ComboBox
@content={{this.recurringOptions}}
@value={{this.recurring}}
@onChange={{fn (mut this.recurring)}}
@options={{hash
none="discourse_local_dates.create.form.recurring_none"
}}
class="recurrence-input"
/>
</div>
</div>
{{/unless}}
<div class="picker-panel"> <div class="control-group timezones">
<CalendarDateTimeInput <label>{{i18n
@datePickerId="local-date-create-form" "discourse_local_dates.create.form.timezones_title"
@date={{this.selectedDate}} }}</label>
@time={{this.selectedTime}} <p>{{i18n
@minDate={{this.minDate}} "discourse_local_dates.create.form.timezones_description"
@timeFormat={{this.timeFormat}} }}</p>
@dateFormat={{this.dateFormat}} <div class="controls">
@onChangeDate={{this.changeSelectedDate}} <MultiSelect
@onChangeTime={{this.changeSelectedTime}} @valueProperty={{null}}
/> @nameProperty={{null}}
</div> @content={{this.allTimezones}}
@value={{this.timezones}}
@options={{hash allowAny=false maximum=5}}
class="timezones-input"
/>
</div>
</div>
{{#if this.site.mobileView}} <div class="control-group format">
<TimezoneInput <label>{{i18n
@value={{this.timezone}} "discourse_local_dates.create.form.format_title"
@options={{hash icon="globe"}} }}</label>
@onChange={{fn (mut this.timezone)}} <p>
/> {{i18n
{{/if}} "discourse_local_dates.create.form.format_description"
</div>
{{#if this.advancedMode}}
<div class="advanced-options">
{{#unless this.isRange}}
<div class="control-group recurrence">
<label class="control-label">
{{i18n "discourse_local_dates.create.form.recurring_title"}}
</label>
<p>{{html-safe
(i18n
"discourse_local_dates.create.form.recurring_description"
)
}}</p>
<div class="controls">
<ComboBox
@content={{this.recurringOptions}}
@value={{this.recurring}}
@onChange={{fn (mut this.recurring)}}
@options={{hash
none="discourse_local_dates.create.form.recurring_none"
}} }}
class="recurrence-input" <a
/> target="_blank"
href="https://momentjs.com/docs/#/parsing/string-format/"
rel="noopener noreferrer"
>
{{icon "circle-question"}}
</a>
</p>
<div class="controls">
<TextField @value={{this.format}} class="format-input" />
</div>
</div>
<div class="control-group">
<ul class="formats">
{{#each this.previewedFormats as |previewedFormat|}}
<li class="format">
<a
class="moment-format"
href
{{on
"click"
(fn this.updateFormat previewedFormat.format)
}}
>
{{previewedFormat.format}}
</a>
<span class="previewed-format">
{{previewedFormat.preview}}
</span>
</li>
{{/each}}
</ul>
</div> </div>
</div> </div>
{{/unless}} {{/if}}
<div class="control-group timezones">
<label>{{i18n
"discourse_local_dates.create.form.timezones_title"
}}</label>
<p>{{i18n
"discourse_local_dates.create.form.timezones_description"
}}</p>
<div class="controls">
<MultiSelect
@valueProperty={{null}}
@nameProperty={{null}}
@content={{this.allTimezones}}
@value={{this.timezones}}
@options={{hash allowAny=false maximum=5}}
class="timezones-input"
/>
</div>
</div>
<div class="control-group format">
<label>{{i18n
"discourse_local_dates.create.form.format_title"
}}</label>
<p>
{{i18n "discourse_local_dates.create.form.format_description"}}
<a
target="_blank"
href="https://momentjs.com/docs/#/parsing/string-format/"
rel="noopener noreferrer"
>
{{d-icon "circle-question"}}
</a>
</p>
<div class="controls">
<TextField @value={{this.format}} class="format-input" />
</div>
</div>
<div class="control-group">
<ul class="formats">
{{#each this.previewedFormats as |previewedFormat|}}
<li class="format">
<a
class="moment-format"
href
{{on "click" (fn this.updateFormat previewedFormat.format)}}
>
{{previewedFormat.format}}
</a>
<span class="previewed-format">
{{previewedFormat.preview}}
</span>
</li>
{{/each}}
</ul>
</div>
</div> </div>
{{/if}} </:body>
</div>
</:body>
<:footer> <:footer>
{{#if this.isValid}} {{#if this.isValid}}
<DButton <DButton
@action={{this.save}} @action={{this.save}}
@label="discourse_local_dates.create.form.insert" @label="discourse_local_dates.create.form.insert"
class="btn-primary" class="btn-primary"
/> />
{{/if}} {{/if}}
<DButton <DButton
@action={{this.cancel}} @action={{this.cancel}}
@translatedLabel={{i18n "cancel"}} @translatedLabel={{i18n "cancel"}}
class="btn-flat" class="btn-flat"
/> />
<DButton <DButton
@action={{this.toggleAdvancedMode}} @action={{this.toggleAdvancedMode}}
@icon="gear" @icon="gear"
@label={{this.toggleModeBtnLabel}} @label={{this.toggleModeBtnLabel}}
class="btn-default advanced-mode-btn" class="btn-default advanced-mode-btn"
/> />
</:footer> </:footer>
</DModal> </DModal>
</template>
}

View File

@ -1 +1,8 @@
<ComposerPresenceDisplay @model={{@outletArgs.model}} /> import ComposerPresenceDisplay from "discourse/plugins/discourse-presence/discourse/components/composer-presence-display";
const Presence = <template>
<div class="before-composer-controls-outlet presence">
<ComposerPresenceDisplay @model={{@outletArgs.model}} />
</div>
</template>;
export default Presence;

View File

@ -1 +1,8 @@
<TopicPresenceDisplay @topic={{@outletArgs.model}} /> import TopicPresenceDisplay from "discourse/plugins/discourse-presence/discourse/components/topic-presence-display";
const Presence = <template>
<div class="topic-above-footer-buttons-outlet presence">
<TopicPresenceDisplay @topic={{@outletArgs.model}} />
</div>
</template>;
export default Presence;

View File

@ -1,13 +1,21 @@
import Component from "@ember/component"; import Component from "@ember/component";
import { fn, get } from "@ember/helper";
import { on } from "@ember/modifier";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { classify } from "@ember/string"; import { classify } from "@ember/string";
import { htmlSafe } from "@ember/template"; import { htmlSafe } from "@ember/template";
import { eq } from "truth-helpers";
import DModal from "discourse/components/d-modal";
import concatClass from "discourse/helpers/concat-class";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import loadScript from "discourse/lib/load-script"; import loadScript from "discourse/lib/load-script";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import PollBreakdownChart from "discourse/plugins/poll/discourse/components/poll-breakdown-chart";
import PollBreakdownOption from "discourse/plugins/poll/discourse/components/poll-breakdown-option";
export default class PollBreakdownModal extends Component { export default class PollBreakdownModal extends Component {
@service dialog; @service dialog;
@ -88,85 +96,87 @@ export default class PollBreakdownModal extends Component {
onSelectPanel(panel) { onSelectPanel(panel) {
this.set("displayMode", panel.id); this.set("displayMode", panel.id);
} }
<template>
{{! template-lint-disable no-invalid-interactive }}
<DModal
@title={{i18n "poll.breakdown.title"}}
@closeModal={{@closeModal}}
class="poll-breakdown has-tabs"
>
<:headerBelowTitle>
<ul class="modal-tabs">
<li
class={{concatClass
"modal-tab percentage"
(if (eq this.displayMode "percentage") "is-active")
}}
{{on "click" (fn (mut this.displayMode) "percentage")}}
>{{i18n "poll.breakdown.percentage"}}</li>
<li
class={{concatClass
"modal-tab count"
(if (eq this.displayMode "count") "is-active")
}}
{{on "click" (fn (mut this.displayMode) "count")}}
>{{i18n "poll.breakdown.count"}}</li>
</ul>
</:headerBelowTitle>
<:body>
<div class="poll-breakdown-sidebar">
<p class="poll-breakdown-title">
{{this.title}}
</p>
<div class="poll-breakdown-total-votes">{{i18n
"poll.breakdown.votes"
count=this.model.poll.voters
}}</div>
<ul class="poll-breakdown-options">
{{#each this.model.poll.options as |option index|}}
<PollBreakdownOption
@option={{option}}
@index={{index}}
@totalVotes={{this.totalVotes}}
@optionsCount={{this.model.poll.options.length}}
@displayMode={{this.displayMode}}
@highlightedOption={{this.highlightedOption}}
@onMouseOver={{fn (mut this.highlightedOption) index}}
@onMouseOut={{fn (mut this.highlightedOption) null}}
/>
{{/each}}
</ul>
</div>
<div class="poll-breakdown-body">
<div class="poll-breakdown-body-header">
<label class="poll-breakdown-body-header-label">{{i18n
"poll.breakdown.breakdown"
}}</label>
<ComboBox
@content={{this.groupableUserFields}}
@value={{this.groupedBy}}
@nameProperty="label"
@onChange={{this.setGrouping}}
class="poll-breakdown-dropdown"
/>
</div>
<div class="poll-breakdown-charts">
{{#each this.charts as |chart|}}
<PollBreakdownChart
@group={{get chart "group"}}
@options={{get chart "options"}}
@displayMode={{this.displayMode}}
@highlightedOption={{this.highlightedOption}}
@setHighlightedOption={{fn (mut this.highlightedOption)}}
/>
{{/each}}
</div>
</div>
</:body>
</DModal>
</template>
} }
{{! template-lint-disable no-invalid-interactive }}
<DModal
@title={{i18n "poll.breakdown.title"}}
@closeModal={{@closeModal}}
class="poll-breakdown has-tabs"
>
<:headerBelowTitle>
<ul class="modal-tabs">
<li
class={{concat-class
"modal-tab percentage"
(if (eq this.displayMode "percentage") "is-active")
}}
{{on "click" (fn (mut this.displayMode) "percentage")}}
>{{i18n "poll.breakdown.percentage"}}</li>
<li
class={{concat-class
"modal-tab count"
(if (eq this.displayMode "count") "is-active")
}}
{{on "click" (fn (mut this.displayMode) "count")}}
>{{i18n "poll.breakdown.count"}}</li>
</ul>
</:headerBelowTitle>
<:body>
<div class="poll-breakdown-sidebar">
<p class="poll-breakdown-title">
{{this.title}}
</p>
<div class="poll-breakdown-total-votes">{{i18n
"poll.breakdown.votes"
count=this.model.poll.voters
}}</div>
<ul class="poll-breakdown-options">
{{#each this.model.poll.options as |option index|}}
<PollBreakdownOption
@option={{option}}
@index={{index}}
@totalVotes={{this.totalVotes}}
@optionsCount={{this.model.poll.options.length}}
@displayMode={{this.displayMode}}
@highlightedOption={{this.highlightedOption}}
@onMouseOver={{fn (mut this.highlightedOption) index}}
@onMouseOut={{fn (mut this.highlightedOption) null}}
/>
{{/each}}
</ul>
</div>
<div class="poll-breakdown-body">
<div class="poll-breakdown-body-header">
<label class="poll-breakdown-body-header-label">{{i18n
"poll.breakdown.breakdown"
}}</label>
<ComboBox
@content={{this.groupableUserFields}}
@value={{this.groupedBy}}
@nameProperty="label"
@onChange={{this.setGrouping}}
class="poll-breakdown-dropdown"
/>
</div>
<div class="poll-breakdown-charts">
{{#each this.charts as |chart|}}
<PollBreakdownChart
@group={{get chart "group"}}
@options={{get chart "options"}}
@displayMode={{this.displayMode}}
@highlightedOption={{this.highlightedOption}}
@setHighlightedOption={{fn (mut this.highlightedOption)}}
/>
{{/each}}
</div>
</div>
</:body>
</DModal>

View File

@ -1,10 +1,24 @@
import Component from "@ember/component"; import Component, { Input, Textarea } from "@ember/component";
import { fn } from "@ember/helper";
import { on } from "@ember/modifier";
import EmberObject, { action } from "@ember/object"; import EmberObject, { action } from "@ember/object";
import { gt, or } from "@ember/object/computed"; import { gt, or } from "@ember/object/computed";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { observes } from "@ember-decorators/object"; import { observes } from "@ember-decorators/object";
import { and, not } from "truth-helpers";
import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal";
import DToggleSwitch from "discourse/components/d-toggle-switch";
import DateTimeInput from "discourse/components/date-time-input";
import InputTip from "discourse/components/input-tip";
import RadioButton from "discourse/components/radio-button";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse/helpers/d-icon";
import discourseComputed from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import autoFocus from "discourse/modifiers/auto-focus";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import ComboBox from "select-kit/components/combo-box";
import GroupChooser from "select-kit/components/group-chooser";
export const BAR_CHART_TYPE = "bar"; export const BAR_CHART_TYPE = "bar";
export const PIE_CHART_TYPE = "pie"; export const PIE_CHART_TYPE = "pie";
@ -396,275 +410,277 @@ export default class PollUiBuilderModal extends Component {
togglePublic() { togglePublic() {
this.set("publicPoll", !this.publicPoll); this.set("publicPoll", !this.publicPoll);
} }
}
<DModal <template>
@title={{i18n "poll.ui_builder.title"}} <DModal
@closeModal={{@closeModal}} @title={{i18n "poll.ui_builder.title"}}
@inline={{@inline}} @closeModal={{@closeModal}}
class="poll-ui-builder" @inline={{@inline}}
> class="poll-ui-builder"
<:body> >
<ul class="nav nav-pills poll-type"> <:body>
<li> <ul class="nav nav-pills poll-type">
<DButton <li>
@action={{fn this.updatePollType "regular"}}
class={{concatClass
"poll-type-value poll-type-value-regular"
(if this.isRegular "active")
}}
>
{{i18n "poll.ui_builder.poll_type.regular"}}
</DButton>
</li>
<li>
<DButton
@action={{fn this.updatePollType "multiple"}}
class={{concatClass
"poll-type-value poll-type-value-multiple"
(if this.isMultiple "active")
}}
>
{{i18n "poll.ui_builder.poll_type.multiple"}}
</DButton>
</li>
{{#if this.showNumber}}
<li>
<DButton
@action={{fn this.updatePollType "number"}}
class={{concatClass
"poll-type-value poll-type-value-number"
(if this.isNumber "active")
}}
>
{{i18n "poll.ui_builder.poll_type.number"}}
</DButton>
</li>
{{/if}}
{{#if this.showRankedChoice}}
<li>
<DButton
@action={{fn this.updatePollType "ranked_choice"}}
class={{concatClass
"poll-type-value poll-type-value-ranked-choice"
(if this.isRankedChoice "active")
}}
>
{{i18n "poll.ui_builder.poll_type.ranked_choice"}}
</DButton>
</li>
{{/if}}
</ul>
{{#if this.showAdvanced}}
<div class="input-group poll-title">
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_title.label"
}}</label>
<Input @value={{this.pollTitle}} />
</div>
{{/if}}
{{#unless this.isNumber}}
<div class="poll-options">
{{#if this.showAdvanced}}
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_options.label"
}}</label>
<Textarea
@value={{this.pollOptionsText}}
{{on "input" this.onOptionsTextChange}}
/>
{{#if this.showMinNumOfOptionsValidation}}
{{#unless this.minNumOfOptionsValidation.ok}}
<InputTip @validation={{this.minNumOfOptionsValidation}} />
{{/unless}}
{{/if}}
{{else}}
{{#each this.pollOptions as |option index|}}
<div class="input-group poll-option-value">
<input
type="text"
value={{option.value}}
{{auto-focus}}
{{on "input" (fn this.updateValue option)}}
{{on "keydown" (fn this.onInputKeydown index)}}
/>
{{#if this.canRemoveOption}}
<DButton
@icon="trash-can"
@action={{fn this.removeOption option}}
/>
{{/if}}
</div>
{{/each}}
<div class="poll-option-controls">
<DButton <DButton
@icon="plus" @action={{fn this.updatePollType "regular"}}
@label="poll.ui_builder.poll_options.add" class={{concatClass
@action={{fn this.addOption -1}} "poll-type-value poll-type-value-regular"
class="btn-default poll-option-add" (if this.isRegular "active")
/> }}
{{#if >
(and {{i18n "poll.ui_builder.poll_type.regular"}}
this.showMinNumOfOptionsValidation </DButton>
(not this.minNumOfOptionsValidation.ok) </li>
) <li>
}} <DButton
<InputTip @validation={{this.minNumOfOptionsValidation}} /> @action={{fn this.updatePollType "multiple"}}
class={{concatClass
"poll-type-value poll-type-value-multiple"
(if this.isMultiple "active")
}}
>
{{i18n "poll.ui_builder.poll_type.multiple"}}
</DButton>
</li>
{{#if this.showNumber}}
<li>
<DButton
@action={{fn this.updatePollType "number"}}
class={{concatClass
"poll-type-value poll-type-value-number"
(if this.isNumber "active")
}}
>
{{i18n "poll.ui_builder.poll_type.number"}}
</DButton>
</li>
{{/if}}
{{#if this.showRankedChoice}}
<li>
<DButton
@action={{fn this.updatePollType "ranked_choice"}}
class={{concatClass
"poll-type-value poll-type-value-ranked-choice"
(if this.isRankedChoice "active")
}}
>
{{i18n "poll.ui_builder.poll_type.ranked_choice"}}
</DButton>
</li>
{{/if}}
</ul>
{{#if this.showAdvanced}}
<div class="input-group poll-title">
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_title.label"
}}</label>
<Input @value={{this.pollTitle}} />
</div>
{{/if}}
{{#unless this.isNumber}}
<div class="poll-options">
{{#if this.showAdvanced}}
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_options.label"
}}</label>
<Textarea
@value={{this.pollOptionsText}}
{{on "input" this.onOptionsTextChange}}
/>
{{#if this.showMinNumOfOptionsValidation}}
{{#unless this.minNumOfOptionsValidation.ok}}
<InputTip @validation={{this.minNumOfOptionsValidation}} />
{{/unless}}
{{/if}}
{{else}}
{{#each this.pollOptions as |option index|}}
<div class="input-group poll-option-value">
<input
type="text"
value={{option.value}}
{{autoFocus}}
{{on "input" (fn this.updateValue option)}}
{{on "keydown" (fn this.onInputKeydown index)}}
/>
{{#if this.canRemoveOption}}
<DButton
@icon="trash-can"
@action={{fn this.removeOption option}}
/>
{{/if}}
</div>
{{/each}}
<div class="poll-option-controls">
<DButton
@icon="plus"
@label="poll.ui_builder.poll_options.add"
@action={{fn this.addOption -1}}
class="btn-default poll-option-add"
/>
{{#if
(and
this.showMinNumOfOptionsValidation
(not this.minNumOfOptionsValidation.ok)
)
}}
<InputTip @validation={{this.minNumOfOptionsValidation}} />
{{/if}}
</div>
{{/if}} {{/if}}
</div> </div>
{{/if}} {{/unless}}
</div>
{{/unless}}
{{#unless this.rankedChoiceOrRegular}} {{#unless this.rankedChoiceOrRegular}}
<div class="options"> <div class="options">
<div class="input-group poll-number"> <div class="input-group poll-number">
<label class="input-group-label">{{i18n <label class="input-group-label">{{i18n
"poll.ui_builder.poll_config.min" "poll.ui_builder.poll_config.min"
}}</label> }}</label>
<Input <Input
@type="number" @type="number"
@value={{this.pollMin}} @value={{this.pollMin}}
class="poll-options-min" class="poll-options-min"
min="1" min="1"
/>
</div>
<div class="input-group poll-number">
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_config.max"
}}</label>
<Input
@type="number"
@value={{this.pollMax}}
class="poll-options-max"
min="1"
/>
</div>
{{#if this.isNumber}}
<div class="input-group poll-number">
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_config.step"
}}</label>
<Input
@type="number"
@value={{this.pollStep}}
min="1"
class="poll-options-step"
/>
</div>
{{/if}}
</div>
{{#unless this.minMaxValueValidation.ok}}
<InputTip @validation={{this.minMaxValueValidation}} />
{{/unless}}
{{/unless}}
<div class="input-group poll-public">
<DToggleSwitch
@state={{this.publicPoll}}
@label="poll.ui_builder.poll_public.label"
class="poll-toggle-public"
{{on "click" this.togglePublic}}
/> />
</div> </div>
<div class="input-group poll-number"> {{#if this.showAdvanced}}
<label class="input-group-label">{{i18n <div class="input-group poll-allowed-groups">
"poll.ui_builder.poll_config.max"
}}</label>
<Input
@type="number"
@value={{this.pollMax}}
class="poll-options-max"
min="1"
/>
</div>
{{#if this.isNumber}}
<div class="input-group poll-number">
<label class="input-group-label">{{i18n <label class="input-group-label">{{i18n
"poll.ui_builder.poll_config.step" "poll.ui_builder.poll_groups.label"
}}</label> }}</label>
<Input <GroupChooser
@type="number" @content={{this.siteGroups}}
@value={{this.pollStep}} @value={{this.pollGroups}}
min="1" @onChange={{fn (mut this.pollGroups)}}
class="poll-options-step" @labelProperty="name"
@valueProperty="name"
/> />
</div> </div>
<div class="input-group poll-date">
<label class="input-group-label">{{i18n
"poll.ui_builder.automatic_close.label"
}}</label>
<DateTimeInput
@date={{this.pollAutoClose}}
@onChange={{fn (mut this.pollAutoClose)}}
@clearable={{true}}
@useGlobalPickerContainer={{true}}
/>
</div>
<div class="input-group poll-select">
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_result.label"
}}</label>
<ComboBox
@content={{this.pollResults}}
@value={{this.pollResult}}
@valueProperty="value"
@onChange={{fn (mut this.pollResult)}}
class="poll-result"
/>
</div>
{{#unless this.rankedChoiceOrNumber}}
<div class="input-group poll-select column">
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_chart_type.label"
}}</label>
<div class="radio-group">
<RadioButton
@id="poll-chart-type-bar"
@name="poll-chart-type"
@value="bar"
@selection={{this.chartType}}
/>
<label for="poll-chart-type-bar">{{icon "chart-bar"}}
{{i18n "poll.ui_builder.poll_chart_type.bar"}}</label>
</div>
<div class="radio-group">
<RadioButton
@id="poll-chart-type-pie"
@name="poll-chart-type"
@value="pie"
@selection={{this.chartType}}
/>
<label for="poll-chart-type-pie">{{icon "chart-pie"}}
{{i18n "poll.ui_builder.poll_chart_type.pie"}}</label>
</div>
</div>
{{/unless}}
{{/if}} {{/if}}
</div> </:body>
<:footer>
{{#unless this.minMaxValueValidation.ok}} <DButton
<InputTip @validation={{this.minMaxValueValidation}} /> @action={{this.insertPoll}}
{{/unless}} @icon="chart-bar"
{{/unless}} @label="poll.ui_builder.insert"
@disabled={{this.disableInsert}}
<div class="input-group poll-public"> class="btn-primary insert-poll"
<DToggleSwitch
@state={{this.publicPoll}}
@label="poll.ui_builder.poll_public.label"
class="poll-toggle-public"
{{on "click" this.togglePublic}}
/>
</div>
{{#if this.showAdvanced}}
<div class="input-group poll-allowed-groups">
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_groups.label"
}}</label>
<GroupChooser
@content={{this.siteGroups}}
@value={{this.pollGroups}}
@onChange={{fn (mut this.pollGroups)}}
@labelProperty="name"
@valueProperty="name"
/> />
</div>
<div class="input-group poll-date"> <DButton @label="cancel" @action={{@closeModal}} class="btn-flat" />
<label class="input-group-label">{{i18n
"poll.ui_builder.automatic_close.label" <DButton
}}</label> @action={{this.toggleAdvanced}}
<DateTimeInput @icon="gear"
@date={{this.pollAutoClose}} @title={{if
@onChange={{fn (mut this.pollAutoClose)}} this.showAdvanced
@clearable={{true}} "poll.ui_builder.hide_advanced"
@useGlobalPickerContainer={{true}} "poll.ui_builder.show_advanced"
}}
class="btn-default show-advanced"
/> />
</div>
<div class="input-group poll-select"> </:footer>
<label class="input-group-label">{{i18n </DModal>
"poll.ui_builder.poll_result.label" </template>
}}</label> }
<ComboBox
@content={{this.pollResults}}
@value={{this.pollResult}}
@valueProperty="value"
@onChange={{fn (mut this.pollResult)}}
class="poll-result"
/>
</div>
{{#unless this.rankedChoiceOrNumber}}
<div class="input-group poll-select column">
<label class="input-group-label">{{i18n
"poll.ui_builder.poll_chart_type.label"
}}</label>
<div class="radio-group">
<RadioButton
@id="poll-chart-type-bar"
@name="poll-chart-type"
@value="bar"
@selection={{this.chartType}}
/>
<label for="poll-chart-type-bar">{{d-icon "chart-bar"}}
{{i18n "poll.ui_builder.poll_chart_type.bar"}}</label>
</div>
<div class="radio-group">
<RadioButton
@id="poll-chart-type-pie"
@name="poll-chart-type"
@value="pie"
@selection={{this.chartType}}
/>
<label for="poll-chart-type-pie">{{d-icon "chart-pie"}}
{{i18n "poll.ui_builder.poll_chart_type.pie"}}</label>
</div>
</div>
{{/unless}}
{{/if}}
</:body>
<:footer>
<DButton
@action={{this.insertPoll}}
@icon="chart-bar"
@label="poll.ui_builder.insert"
@disabled={{this.disableInsert}}
class="btn-primary insert-poll"
/>
<DButton @label="cancel" @action={{@closeModal}} class="btn-flat" />
<DButton
@action={{this.toggleAdvanced}}
@icon="gear"
@title={{if
this.showAdvanced
"poll.ui_builder.hide_advanced"
"poll.ui_builder.show_advanced"
}}
class="btn-default show-advanced"
/>
</:footer>
</DModal>

View File

@ -1,13 +1,15 @@
import { click, render } from "@ember/test-helpers"; import { click, render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import PollButtonsDropdown from "discourse/plugins/poll/discourse/components/poll-buttons-dropdown";
module("Poll | Component | poll-buttons-dropdown", function (hooks) { module("Poll | Component | poll-buttons-dropdown", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("Renders a clickable dropdown menu with a close option", async function (assert) { test("Renders a clickable dropdown menu with a close option", async function (assert) {
const self = this;
this.siteSettings.data_explorer_enabled = true; this.siteSettings.data_explorer_enabled = true;
this.siteSettings.poll_export_data_explorer_query_id = 18; this.siteSettings.poll_export_data_explorer_query_id = 18;
this.currentUser.setProperties({ admin: true }); this.currentUser.setProperties({ admin: true });
@ -23,16 +25,20 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
dropDownClick: () => {}, dropDownClick: () => {},
}); });
await render(hbs`<PollButtonsDropdown await render(
@closed={{this.closed}} <template>
@voters={{this.voters}} <PollButtonsDropdown
@isStaff={{this.isStaff}} @closed={{self.closed}}
@isMe={{this.isMe}} @voters={{self.voters}}
@topicArchived={{this.topicArchived}} @isStaff={{self.isStaff}}
@groupableUserFields={{this.groupableUserFields}} @isMe={{self.isMe}}
@isAutomaticallyClosed={{this.isAutomaticallyClosed}} @topicArchived={{self.topicArchived}}
@dropDownClick={{this.dropDownClick}} @groupableUserFields={{self.groupableUserFields}}
/>`); @isAutomaticallyClosed={{self.isAutomaticallyClosed}}
@dropDownClick={{self.dropDownClick}}
/>
</template>
);
await click(".widget-dropdown-header"); await click(".widget-dropdown-header");
@ -46,6 +52,8 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
}); });
test("Renders a show-tally button when poll is a bar chart", async function (assert) { test("Renders a show-tally button when poll is a bar chart", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
closed: false, closed: false,
voters: 2, voters: 2,
@ -58,17 +66,21 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
availableDisplayMode: "showTally", availableDisplayMode: "showTally",
}); });
await render(hbs`<PollButtonsDropdown await render(
@closed={{this.closed}} <template>
@voters={{this.voters}} <PollButtonsDropdown
@isStaff={{this.isStaff}} @closed={{self.closed}}
@isMe={{this.isMe}} @voters={{self.voters}}
@topicArchived={{this.topicArchived}} @isStaff={{self.isStaff}}
@groupableUserFields={{this.groupableUserFields}} @isMe={{self.isMe}}
@isAutomaticallyClosed={{this.isAutomaticallyClosed}} @topicArchived={{self.topicArchived}}
@dropDownClick={{this.dropDownClick}} @groupableUserFields={{self.groupableUserFields}}
@availableDisplayMode={{this.availableDisplayMode}} @isAutomaticallyClosed={{self.isAutomaticallyClosed}}
/>`); @dropDownClick={{self.dropDownClick}}
@availableDisplayMode={{self.availableDisplayMode}}
/>
</template>
);
await click(".widget-dropdown-header"); await click(".widget-dropdown-header");
@ -82,6 +94,8 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
}); });
test("Renders a single button when there is only one authorised action", async function (assert) { test("Renders a single button when there is only one authorised action", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
closed: false, closed: false,
voters: 2, voters: 2,
@ -93,16 +107,20 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
dropDownClick: () => {}, dropDownClick: () => {},
}); });
await render(hbs`<PollButtonsDropdown await render(
@closed={{this.closed}} <template>
@voters={{this.voters}} <PollButtonsDropdown
@isStaff={{this.isStaff}} @closed={{self.closed}}
@isMe={{this.isMe}} @voters={{self.voters}}
@topicArchived={{this.topicArchived}} @isStaff={{self.isStaff}}
@groupableUserFields={{this.groupableUserFields}} @isMe={{self.isMe}}
@isAutomaticallyClosed={{this.isAutomaticallyClosed}} @topicArchived={{self.topicArchived}}
@dropDownClick={{this.dropDownClick}} @groupableUserFields={{self.groupableUserFields}}
/>`); @isAutomaticallyClosed={{self.isAutomaticallyClosed}}
@dropDownClick={{self.dropDownClick}}
/>
</template>
);
assert.dom(".widget-dropdown-header").doesNotExist(); assert.dom(".widget-dropdown-header").doesNotExist();
assert.dom("button.widget-button").exists({ count: 1 }); assert.dom("button.widget-button").exists({ count: 1 });
@ -115,6 +133,8 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
}); });
test("Doesn't render a button when user has no authorised actions", async function (assert) { test("Doesn't render a button when user has no authorised actions", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
closed: false, closed: false,
voters: 0, voters: 0,
@ -126,16 +146,20 @@ module("Poll | Component | poll-buttons-dropdown", function (hooks) {
dropDownClick: () => {}, dropDownClick: () => {},
}); });
await render(hbs`<PollButtonsDropdown await render(
@closed={{this.closed}} <template>
@voters={{this.voters}} <PollButtonsDropdown
@isStaff={{this.isStaff}} @closed={{self.closed}}
@isMe={{this.isMe}} @voters={{self.voters}}
@topicArchived={{this.topicArchived}} @isStaff={{self.isStaff}}
@groupableUserFields={{this.groupableUserFields}} @isMe={{self.isMe}}
@isAutomaticallyClosed={{this.isAutomaticallyClosed}} @topicArchived={{self.topicArchived}}
@dropDownClick={{this.dropDownClick}} @groupableUserFields={{self.groupableUserFields}}
/>`); @isAutomaticallyClosed={{self.isAutomaticallyClosed}}
@dropDownClick={{self.dropDownClick}}
/>
</template>
);
assert.dom(".widget-dropdown-header").doesNotExist(); assert.dom(".widget-dropdown-header").doesNotExist();
assert.dom("button.widget-button").doesNotExist(); assert.dom("button.widget-button").doesNotExist();

View File

@ -1,8 +1,8 @@
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import PollInfo from "discourse/plugins/poll/discourse/components/poll-info";
const OPTIONS = [ const OPTIONS = [
{ id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 2, rank: 0 }, { id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 2, rank: 0 },
@ -14,6 +14,8 @@ module("Poll | Component | poll-info", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("public multiple poll with results anytime", async function (assert) { test("public multiple poll with results anytime", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
isMultiple: true, isMultiple: true,
min: 1, min: 1,
@ -29,20 +31,24 @@ module("Poll | Component | poll-info", function (hooks) {
voters: [], voters: [],
}); });
await render(hbs`<PollInfo await render(
@options={{this.options}} <template>
@min={{this.min}} <PollInfo
@max={{this.max}} @options={{self.options}}
@isMultiple={{this.isMultiple}} @min={{self.min}}
@close={{this.close}} @max={{self.max}}
@closed={{this.closed}} @isMultiple={{self.isMultiple}}
@results={{this.results}} @close={{self.close}}
@showResults={{this.showResults}} @closed={{self.closed}}
@postUserId={{this.postUserId}} @results={{self.results}}
@isPublic={{this.isPublic}} @showResults={{self.showResults}}
@hasVoted={{this.hasVoted}} @postUserId={{self.postUserId}}
@voters={{this.voters}} @isPublic={{self.isPublic}}
/>`); @hasVoted={{self.hasVoted}}
@voters={{self.voters}}
/>
</template>
);
assert.dom(".poll-info_instructions li.multiple-help-text").hasText( assert.dom(".poll-info_instructions li.multiple-help-text").hasText(
i18n("poll.multiple.help.up_to_max_options", { i18n("poll.multiple.help.up_to_max_options", {
@ -60,6 +66,8 @@ module("Poll | Component | poll-info", function (hooks) {
}); });
test("public multiple poll with results only shown on vote", async function (assert) { test("public multiple poll with results only shown on vote", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
isMultiple: true, isMultiple: true,
min: 1, min: 1,
@ -75,20 +83,24 @@ module("Poll | Component | poll-info", function (hooks) {
voters: [], voters: [],
}); });
await render(hbs`<PollInfo await render(
@options={{this.options}} <template>
@min={{this.min}} <PollInfo
@max={{this.max}} @options={{self.options}}
@isMultiple={{this.isMultiple}} @min={{self.min}}
@close={{this.close}} @max={{self.max}}
@closed={{this.closed}} @isMultiple={{self.isMultiple}}
@results={{this.results}} @close={{self.close}}
@showResults={{this.showResults}} @closed={{self.closed}}
@postUserId={{this.postUserId}} @results={{self.results}}
@isPublic={{this.isPublic}} @showResults={{self.showResults}}
@hasVoted={{this.hasVoted}} @postUserId={{self.postUserId}}
@voters={{this.voters}} @isPublic={{self.isPublic}}
/>`); @hasVoted={{self.hasVoted}}
@voters={{self.voters}}
/>
</template>
);
assert.dom(".poll-info_instructions li.multiple-help-text").hasText( assert.dom(".poll-info_instructions li.multiple-help-text").hasText(
i18n("poll.multiple.help.up_to_max_options", { i18n("poll.multiple.help.up_to_max_options", {

View File

@ -1,8 +1,8 @@
import { click, render } from "@ember/test-helpers"; import { click, render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import PollOptions from "discourse/plugins/poll/discourse/components/poll-options";
const OPTIONS = [ const OPTIONS = [
{ id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 0, rank: 0 }, { id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 0, rank: 0 },
@ -29,6 +29,8 @@ module("Poll | Component | poll-options", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("single, not selected", async function (assert) { test("single, not selected", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
isCheckbox: false, isCheckbox: false,
isRankedChoice: false, isRankedChoice: false,
@ -37,19 +39,25 @@ module("Poll | Component | poll-options", function (hooks) {
votes: [], votes: [],
}); });
await render(hbs`<PollOptions await render(
@isCheckbox={{this.isCheckbox}} <template>
@isRankedChoice={{this.isRankedChoice}} <PollOptions
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}} @isCheckbox={{self.isCheckbox}}
@options={{this.options}} @isRankedChoice={{self.isRankedChoice}}
@votes={{this.votes}} @ranked_choice_dropdown_content={{self.ranked_choice_dropdown_content}}
@sendRadioClick={{this.toggleOption}} @options={{self.options}}
/>`); @votes={{self.votes}}
@sendRadioClick={{self.toggleOption}}
/>
</template>
);
assert.dom("li .d-icon-far-circle:nth-of-type(1)").exists({ count: 3 }); assert.dom("li .d-icon-far-circle:nth-of-type(1)").exists({ count: 3 });
}); });
test("single, selected", async function (assert) { test("single, selected", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
isCheckbox: false, isCheckbox: false,
isRankedChoice: false, isRankedChoice: false,
@ -58,19 +66,25 @@ module("Poll | Component | poll-options", function (hooks) {
votes: ["6c986ebcde3d5822a6e91a695c388094"], votes: ["6c986ebcde3d5822a6e91a695c388094"],
}); });
await render(hbs`<PollOptions await render(
@isCheckbox={{this.isCheckbox}} <template>
@isRankedChoice={{this.isRankedChoice}} <PollOptions
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}} @isCheckbox={{self.isCheckbox}}
@options={{this.options}} @isRankedChoice={{self.isRankedChoice}}
@votes={{this.votes}} @ranked_choice_dropdown_content={{self.ranked_choice_dropdown_content}}
@sendRadioClick={{this.toggleOption}} @options={{self.options}}
/>`); @votes={{self.votes}}
@sendRadioClick={{self.toggleOption}}
/>
</template>
);
assert.dom("li .d-icon-circle:nth-of-type(1)").exists({ count: 1 }); assert.dom("li .d-icon-circle:nth-of-type(1)").exists({ count: 1 });
}); });
test("multi, not selected", async function (assert) { test("multi, not selected", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
isCheckbox: true, isCheckbox: true,
isRankedChoice: false, isRankedChoice: false,
@ -79,19 +93,25 @@ module("Poll | Component | poll-options", function (hooks) {
votes: [], votes: [],
}); });
await render(hbs`<PollOptions await render(
@isCheckbox={{this.isCheckbox}} <template>
@isRankedChoice={{this.isRankedChoice}} <PollOptions
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}} @isCheckbox={{self.isCheckbox}}
@options={{this.options}} @isRankedChoice={{self.isRankedChoice}}
@votes={{this.votes}} @ranked_choice_dropdown_content={{self.ranked_choice_dropdown_content}}
@sendRadioClick={{this.toggleOption}} @options={{self.options}}
/>`); @votes={{self.votes}}
@sendRadioClick={{self.toggleOption}}
/>
</template>
);
assert.dom("li .d-icon-far-square:nth-of-type(1)").exists({ count: 3 }); assert.dom("li .d-icon-far-square:nth-of-type(1)").exists({ count: 3 });
}); });
test("multi, selected", async function (assert) { test("multi, selected", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
isCheckbox: true, isCheckbox: true,
isRankedChoice: false, isRankedChoice: false,
@ -100,14 +120,18 @@ module("Poll | Component | poll-options", function (hooks) {
votes: ["6c986ebcde3d5822a6e91a695c388094"], votes: ["6c986ebcde3d5822a6e91a695c388094"],
}); });
await render(hbs`<PollOptions await render(
@isCheckbox={{this.isCheckbox}} <template>
@isRankedChoice={{this.isRankedChoice}} <PollOptions
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}} @isCheckbox={{self.isCheckbox}}
@options={{this.options}} @isRankedChoice={{self.isRankedChoice}}
@votes={{this.votes}} @ranked_choice_dropdown_content={{self.ranked_choice_dropdown_content}}
@sendRadioClick={{this.toggleOption}} @options={{self.options}}
/>`); @votes={{self.votes}}
@sendRadioClick={{self.toggleOption}}
/>
</template>
);
assert assert
.dom("li .d-icon-far-square-check:nth-of-type(1)") .dom("li .d-icon-far-square-check:nth-of-type(1)")
@ -115,23 +139,31 @@ module("Poll | Component | poll-options", function (hooks) {
}); });
test("single with images", async function (assert) { test("single with images", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
isCheckbox: false, isCheckbox: false,
options: IMAGE_OPTIONS, options: IMAGE_OPTIONS,
votes: [], votes: [],
}); });
await render(hbs`<PollOptions await render(
@isCheckbox={{this.isCheckbox}} <template>
@options={{this.options}} <PollOptions
@votes={{this.votes}} @isCheckbox={{self.isCheckbox}}
@sendRadioClick={{this.toggleOption}} @options={{self.options}}
/>`); @votes={{self.votes}}
@sendRadioClick={{self.toggleOption}}
/>
</template>
);
assert.dom("li img").exists({ count: 2 }); assert.dom("li img").exists({ count: 2 });
}); });
test("ranked choice - priorities", async function (assert) { test("ranked choice - priorities", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
isCheckbox: false, isCheckbox: false,
isRankedChoice: true, isRankedChoice: true,
@ -140,14 +172,18 @@ module("Poll | Component | poll-options", function (hooks) {
votes: [], votes: [],
}); });
await render(hbs`<PollOptions await render(
@isCheckbox={{this.isCheckbox}} <template>
@isRankedChoice={{this.isRankedChoice}} <PollOptions
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}} @isCheckbox={{self.isCheckbox}}
@options={{this.options}} @isRankedChoice={{self.isRankedChoice}}
@votes={{this.votes}} @ranked_choice_dropdown_content={{self.ranked_choice_dropdown_content}}
@sendRadioClick={{this.toggleOption}} @options={{self.options}}
/>`); @votes={{self.votes}}
@sendRadioClick={{self.toggleOption}}
/>
</template>
);
await click( await click(
`.ranked-choice-poll-option[data-poll-option-id='${OPTIONS[0].id}'] button` `.ranked-choice-poll-option[data-poll-option-id='${OPTIONS[0].id}'] button`

View File

@ -1,7 +1,7 @@
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import PollResultsPie from "discourse/plugins/poll/discourse/components/poll-results-pie";
const OPTIONS = [ const OPTIONS = [
{ id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 3, rank: 0 }, { id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 3, rank: 0 },
@ -15,13 +15,17 @@ module("Poll | Component | poll-results-pie", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("Renders the pie chart Component correctly", async function (assert) { test("Renders the pie chart Component correctly", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
id: ID, id: ID,
options: OPTIONS, options: OPTIONS,
}); });
await render( await render(
hbs`<PollResultsPie @id={{this.id}} @options={{this.options}} />` <template>
<PollResultsPie @id={{self.id}} @options={{self.options}} />
</template>
); );
assert.dom("li.legend").exists({ count: 3 }); assert.dom("li.legend").exists({ count: 3 });

View File

@ -1,8 +1,8 @@
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import PollResultsRankedChoice from "discourse/plugins/poll/discourse/components/poll-results-ranked-choice";
const RANKED_CHOICE_OUTCOME = { const RANKED_CHOICE_OUTCOME = {
tied: false, tied: false,
@ -34,12 +34,18 @@ module("Poll | Component | poll-results-ranked-choice", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("Renders the ranked choice results component correctly", async function (assert) { test("Renders the ranked choice results component correctly", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
rankedChoiceOutcome: RANKED_CHOICE_OUTCOME, rankedChoiceOutcome: RANKED_CHOICE_OUTCOME,
}); });
await render( await render(
hbs`<PollResultsRankedChoice @rankedChoiceOutcome={{this.rankedChoiceOutcome}} />` <template>
<PollResultsRankedChoice
@rankedChoiceOutcome={{self.rankedChoiceOutcome}}
/>
</template>
); );
assert assert
@ -56,10 +62,16 @@ module("Poll | Component | poll-results-ranked-choice", function (hooks) {
}); });
test("Renders the ranked choice results component without error when outcome data is empty", async function (assert) { test("Renders the ranked choice results component without error when outcome data is empty", async function (assert) {
const self = this;
this.rankedChoiceOutcome = null; this.rankedChoiceOutcome = null;
await render( await render(
hbs`<PollResultsRankedChoice @rankedChoiceOutcome={{this.rankedChoiceOutcome}} />` <template>
<PollResultsRankedChoice
@rankedChoiceOutcome={{self.rankedChoiceOutcome}}
/>
</template>
); );
assert assert

View File

@ -1,9 +1,9 @@
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import { queryAll } from "discourse/tests/helpers/qunit-helpers"; import { queryAll } from "discourse/tests/helpers/qunit-helpers";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import PollResultsStandard from "discourse/plugins/poll/discourse/components/poll-results-standard";
const TWO_OPTIONS = [ const TWO_OPTIONS = [
{ id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 5, rank: 0 }, { id: "1ddc47be0d2315b9711ee8526ca9d83f", html: "This", votes: 5, rank: 0 },
@ -38,6 +38,8 @@ module("Poll | Component | poll-results-standard", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("Renders the standard results Component correctly", async function (assert) { test("Renders the standard results Component correctly", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
options: TWO_OPTIONS, options: TWO_OPTIONS,
pollName: "Two Choice Poll", pollName: "Two Choice Poll",
@ -50,17 +52,21 @@ module("Poll | Component | poll-results-standard", function (hooks) {
fetchVoters: () => {}, fetchVoters: () => {},
}); });
await render(hbs`<PollResultsStandard await render(
@options={{this.options}} <template>
@pollName={{this.pollName}} <PollResultsStandard
@pollType={{this.pollType}} @options={{self.options}}
@isPublic={{this.isPublic}} @pollName={{self.pollName}}
@postId={{this.postId}} @pollType={{self.pollType}}
@vote={{this.vote}} @isPublic={{self.isPublic}}
@voters={{this.voters}} @postId={{self.postId}}
@votersCount={{this.votersCount}} @vote={{self.vote}}
@fetchVoters={{this.fetchVoters}} @voters={{self.voters}}
/>`); @votersCount={{self.votersCount}}
@fetchVoters={{self.fetchVoters}}
/>
</template>
);
assert.dom(queryAll(".option .percentage")[0]).hasText("56%"); assert.dom(queryAll(".option .percentage")[0]).hasText("56%");
assert.dom(queryAll(".option .percentage")[1]).hasText("44%"); assert.dom(queryAll(".option .percentage")[1]).hasText("44%");
@ -68,6 +74,8 @@ module("Poll | Component | poll-results-standard", function (hooks) {
}); });
test("Omits voters for private polls", async function (assert) { test("Omits voters for private polls", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
options: TWO_OPTIONS, options: TWO_OPTIONS,
pollName: "Two Choice Poll", pollName: "Two Choice Poll",
@ -80,22 +88,28 @@ module("Poll | Component | poll-results-standard", function (hooks) {
fetchVoters: () => {}, fetchVoters: () => {},
}); });
await render(hbs`<PollResultsStandard await render(
@options={{this.options}} <template>
@pollName={{this.pollName}} <PollResultsStandard
@pollType={{this.pollType}} @options={{self.options}}
@isPublic={{this.isPublic}} @pollName={{self.pollName}}
@postId={{this.postId}} @pollType={{self.pollType}}
@vote={{this.vote}} @isPublic={{self.isPublic}}
@voters={{this.voters}} @postId={{self.postId}}
@votersCount={{this.votersCount}} @vote={{self.vote}}
@fetchVoters={{this.fetchVoters}} @voters={{self.voters}}
/>`); @votersCount={{self.votersCount}}
@fetchVoters={{self.fetchVoters}}
/>
</template>
);
assert.dom("ul.poll-voters-list").doesNotExist(); assert.dom("ul.poll-voters-list").doesNotExist();
}); });
test("options in ascending order", async function (assert) { test("options in ascending order", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
options: TWO_OPTIONS_REVERSED, options: TWO_OPTIONS_REVERSED,
pollName: "Two Choice Poll", pollName: "Two Choice Poll",
@ -107,22 +121,28 @@ module("Poll | Component | poll-results-standard", function (hooks) {
fetchVoters: () => {}, fetchVoters: () => {},
}); });
await render(hbs`<PollResultsStandard await render(
@options={{this.options}} <template>
@pollName={{this.pollName}} <PollResultsStandard
@pollType={{this.pollType}} @options={{self.options}}
@postId={{this.postId}} @pollName={{self.pollName}}
@vote={{this.vote}} @pollType={{self.pollType}}
@voters={{this.voters}} @postId={{self.postId}}
@votersCount={{this.votersCount}} @vote={{self.vote}}
@fetchVoters={{this.fetchVoters}} @voters={{self.voters}}
/>`); @votersCount={{self.votersCount}}
@fetchVoters={{self.fetchVoters}}
/>
</template>
);
assert.dom(queryAll(".option .percentage")[0]).hasText("56%"); assert.dom(queryAll(".option .percentage")[0]).hasText("56%");
assert.dom(queryAll(".option .percentage")[1]).hasText("44%"); assert.dom(queryAll(".option .percentage")[1]).hasText("44%");
}); });
test("options in ascending order", async function (assert) { test("options in ascending order", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
options: FIVE_OPTIONS, options: FIVE_OPTIONS,
pollName: "Five Multi Option Poll", pollName: "Five Multi Option Poll",
@ -134,16 +154,20 @@ module("Poll | Component | poll-results-standard", function (hooks) {
fetchVoters: () => {}, fetchVoters: () => {},
}); });
await render(hbs`<PollResultsStandard await render(
@options={{this.options}} <template>
@pollName={{this.pollName}} <PollResultsStandard
@pollType={{this.pollType}} @options={{self.options}}
@postId={{this.postId}} @pollName={{self.pollName}}
@vote={{this.vote}} @pollType={{self.pollType}}
@voters={{this.voters}} @postId={{self.postId}}
@votersCount={{this.votersCount}} @vote={{self.vote}}
@fetchVoters={{this.fetchVoters}} @voters={{self.voters}}
/>`); @votersCount={{self.votersCount}}
@fetchVoters={{self.fetchVoters}}
/>
</template>
);
let percentages = queryAll(".option .percentage"); let percentages = queryAll(".option .percentage");
assert.dom(percentages[0]).hasText("41%"); assert.dom(percentages[0]).hasText("41%");
@ -157,6 +181,8 @@ module("Poll | Component | poll-results-standard", function (hooks) {
}); });
test("options in ascending order, showing absolute vote number", async function (assert) { test("options in ascending order, showing absolute vote number", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
options: FIVE_OPTIONS, options: FIVE_OPTIONS,
pollName: "Five Multi Option Poll", pollName: "Five Multi Option Poll",
@ -169,17 +195,21 @@ module("Poll | Component | poll-results-standard", function (hooks) {
showTally: true, showTally: true,
}); });
await render(hbs`<PollResultsStandard await render(
@options={{this.options}} <template>
@pollName={{this.pollName}} <PollResultsStandard
@pollType={{this.pollType}} @options={{self.options}}
@postId={{this.postId}} @pollName={{self.pollName}}
@vote={{this.vote}} @pollType={{self.pollType}}
@voters={{this.voters}} @postId={{self.postId}}
@votersCount={{this.votersCount}} @vote={{self.vote}}
@fetchVoters={{this.fetchVoters}} @voters={{self.voters}}
@showTally={{this.showTally}} @votersCount={{self.votersCount}}
/>`); @fetchVoters={{self.fetchVoters}}
@showTally={{self.showTally}}
/>
</template>
);
let percentages = queryAll(".option .absolute"); let percentages = queryAll(".option .absolute");
assert.dom(percentages[0]).hasText(i18n("poll.votes", { count: 5 })); assert.dom(percentages[0]).hasText(i18n("poll.votes", { count: 5 }));

View File

@ -1,7 +1,7 @@
import { render } from "@ember/test-helpers"; import { render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import PollResultsTabs from "discourse/plugins/poll/discourse/components/poll-results-tabs";
const TWO_OPTIONS = [ const TWO_OPTIONS = [
{ {
@ -59,6 +59,8 @@ module("Poll | Component | poll-results-tabs", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("Renders one tab for non-ranked-choice poll", async function (assert) { test("Renders one tab for non-ranked-choice poll", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
options: TWO_OPTIONS, options: TWO_OPTIONS,
pollName: "Two Choice Poll", pollName: "Two Choice Poll",
@ -72,23 +74,29 @@ module("Poll | Component | poll-results-tabs", function (hooks) {
fetchVoters: () => {}, fetchVoters: () => {},
}); });
await render(hbs`<PollResultsTabs await render(
@options={{this.options}} <template>
@pollName={{this.pollName}} <PollResultsTabs
@pollType={{this.pollType}} @options={{self.options}}
@isPublic={{this.isPublic}} @pollName={{self.pollName}}
@isRankedChoice={{this.isRankedChoice}} @pollType={{self.pollType}}
@postId={{this.postId}} @isPublic={{self.isPublic}}
@vote={{this.vote}} @isRankedChoice={{self.isRankedChoice}}
@voters={{this.voters}} @postId={{self.postId}}
@votersCount={{this.votersCount}} @vote={{self.vote}}
@fetchVoters={{this.fetchVoters}} @voters={{self.voters}}
/>`); @votersCount={{self.votersCount}}
@fetchVoters={{self.fetchVoters}}
/>
</template>
);
assert.dom("li.tab").exists({ count: 1 }); assert.dom("li.tab").exists({ count: 1 });
}); });
test("Renders two tabs for public ranked choice poll", async function (assert) { test("Renders two tabs for public ranked choice poll", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
options: TWO_OPTIONS, options: TWO_OPTIONS,
pollName: "Two Choice Poll", pollName: "Two Choice Poll",
@ -103,24 +111,30 @@ module("Poll | Component | poll-results-tabs", function (hooks) {
fetchVoters: () => {}, fetchVoters: () => {},
}); });
await render(hbs`<PollResultsTabs await render(
@options={{this.options}} <template>
@pollName={{this.pollName}} <PollResultsTabs
@pollType={{this.pollType}} @options={{self.options}}
@isPublic={{this.isPublic}} @pollName={{self.pollName}}
@isRankedChoice={{this.isRankedChoice}} @pollType={{self.pollType}}
@rankedChoiceOutcome={{this.rankedChoiceOutcome}} @isPublic={{self.isPublic}}
@postId={{this.postId}} @isRankedChoice={{self.isRankedChoice}}
@vote={{this.vote}} @rankedChoiceOutcome={{self.rankedChoiceOutcome}}
@voters={{this.voters}} @postId={{self.postId}}
@votersCount={{this.votersCount}} @vote={{self.vote}}
@fetchVoters={{this.fetchVoters}} @voters={{self.voters}}
/>`); @votersCount={{self.votersCount}}
@fetchVoters={{self.fetchVoters}}
/>
</template>
);
assert.dom("li.tab").exists({ count: 2 }); assert.dom("li.tab").exists({ count: 2 });
}); });
test("Renders one tab for private ranked choice poll", async function (assert) { test("Renders one tab for private ranked choice poll", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
options: TWO_OPTIONS, options: TWO_OPTIONS,
pollName: "Two Choice Poll", pollName: "Two Choice Poll",
@ -135,19 +149,23 @@ module("Poll | Component | poll-results-tabs", function (hooks) {
fetchVoters: () => {}, fetchVoters: () => {},
}); });
await render(hbs`<PollResultsTabs await render(
@options={{this.options}} <template>
@pollName={{this.pollName}} <PollResultsTabs
@pollType={{this.pollType}} @options={{self.options}}
@isPublic={{this.isPublic}} @pollName={{self.pollName}}
@isRankedChoice={{this.isRankedChoice}} @pollType={{self.pollType}}
@rankedChoiceOutcome={{this.rankedChoiceOutcome}} @isPublic={{self.isPublic}}
@postId={{this.postId}} @isRankedChoice={{self.isRankedChoice}}
@vote={{this.vote}} @rankedChoiceOutcome={{self.rankedChoiceOutcome}}
@voters={{this.voters}} @postId={{self.postId}}
@votersCount={{this.votersCount}} @vote={{self.vote}}
@fetchVoters={{this.fetchVoters}} @voters={{self.voters}}
/>`); @votersCount={{self.votersCount}}
@fetchVoters={{self.fetchVoters}}
/>
</template>
);
assert.dom("li.tab").exists({ count: 1 }); assert.dom("li.tab").exists({ count: 1 });
}); });

View File

@ -1,11 +1,11 @@
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { click, render } from "@ember/test-helpers"; import { click, render } from "@ember/test-helpers";
import { TrackedObject } from "@ember-compat/tracked-built-ins"; import { TrackedObject } from "@ember-compat/tracked-built-ins";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender"; import pretender, { response } from "discourse/tests/helpers/create-pretender";
import { i18n } from "discourse-i18n"; import { i18n } from "discourse-i18n";
import Poll from "discourse/plugins/poll/discourse/components/poll";
let requests = 0; let requests = 0;
@ -42,6 +42,8 @@ module("Poll | Component | poll", function (hooks) {
}); });
test("valid ranks with which you can vote", async function (assert) { test("valid ranks with which you can vote", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
post: EmberObject.create({ post: EmberObject.create({
id: 42, id: 42,
@ -96,13 +98,17 @@ module("Poll | Component | poll", function (hooks) {
}), }),
}); });
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`); await render(
<template><Poll @post={{self.post}} @poll={{self.poll}} /></template>
);
assert.dom(".poll-buttons .cast-votes:disabled").doesNotExist(); assert.dom(".poll-buttons .cast-votes:disabled").doesNotExist();
assert.dom(".poll-buttons .cast-votes").exists(); assert.dom(".poll-buttons .cast-votes").exists();
}); });
test("invalid ranks with which you cannot vote", async function (assert) { test("invalid ranks with which you cannot vote", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
post: EmberObject.create({ post: EmberObject.create({
id: 42, id: 42,
@ -141,7 +147,9 @@ module("Poll | Component | poll", function (hooks) {
}), }),
}); });
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`); await render(
<template><Poll @post={{self.post}} @poll={{self.poll}} /></template>
);
await click( await click(
".ranked-choice-poll-option[data-poll-option-id='1f972d1df351de3ce35a787c89faad29'] button", ".ranked-choice-poll-option[data-poll-option-id='1f972d1df351de3ce35a787c89faad29'] button",
@ -171,6 +179,8 @@ module("Poll | Component | poll", function (hooks) {
}); });
test("shows vote", async function (assert) { test("shows vote", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
post: EmberObject.create({ post: EmberObject.create({
id: 42, id: 42,
@ -193,13 +203,17 @@ module("Poll | Component | poll", function (hooks) {
}), }),
}); });
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`); await render(
<template><Poll @post={{self.post}} @poll={{self.poll}} /></template>
);
assert.dom(".results li:nth-of-type(1) .option p").hasText("100% yes"); assert.dom(".results li:nth-of-type(1) .option p").hasText("100% yes");
assert.dom(".results li:nth-of-type(2) .option p").hasText("0% no"); assert.dom(".results li:nth-of-type(2) .option p").hasText("0% no");
}); });
test("does not show results after voting when results are to be shown only on closed", async function (assert) { test("does not show results after voting when results are to be shown only on closed", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
post: EmberObject.create({ post: EmberObject.create({
id: 42, id: 42,
@ -222,13 +236,17 @@ module("Poll | Component | poll", function (hooks) {
}), }),
}); });
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`); await render(
<template><Poll @post={{self.post}} @poll={{self.poll}} /></template>
);
assert.dom("ul.options").exists("options are shown"); assert.dom("ul.options").exists("options are shown");
assert.dom("ul.results").doesNotExist("results are not shown"); assert.dom("ul.results").doesNotExist("results are not shown");
}); });
test("can vote", async function (assert) { test("can vote", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
post: EmberObject.create({ post: EmberObject.create({
id: 42, id: 42,
@ -251,7 +269,9 @@ module("Poll | Component | poll", function (hooks) {
}), }),
}); });
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`); await render(
<template><Poll @post={{self.post}} @poll={{self.poll}} /></template>
);
requests = 0; requests = 0;
@ -268,6 +288,8 @@ module("Poll | Component | poll", function (hooks) {
}); });
test("cannot vote if not member of the right group", async function (assert) { test("cannot vote if not member of the right group", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
post: EmberObject.create({ post: EmberObject.create({
id: 42, id: 42,
@ -291,7 +313,9 @@ module("Poll | Component | poll", function (hooks) {
}), }),
}); });
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`); await render(
<template><Poll @post={{self.post}} @poll={{self.poll}} /></template>
);
requests = 0; requests = 0;
@ -306,6 +330,8 @@ module("Poll | Component | poll", function (hooks) {
}); });
test("voting on a multiple poll with no min attribute", async function (assert) { test("voting on a multiple poll with no min attribute", async function (assert) {
const self = this;
this.setProperties({ this.setProperties({
post: EmberObject.create({ post: EmberObject.create({
id: 42, id: 42,
@ -328,7 +354,9 @@ module("Poll | Component | poll", function (hooks) {
chart_type: "bar", chart_type: "bar",
}), }),
}); });
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`); await render(
<template><Poll @post={{self.post}} @poll={{self.poll}} /></template>
);
assert.dom(".poll-buttons .cast-votes").isDisabled(); assert.dom(".poll-buttons .cast-votes").isDisabled();

View File

@ -1,18 +1,22 @@
import { click, fillIn, render } from "@ember/test-helpers"; import { click, fillIn, render } from "@ember/test-helpers";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit"; import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import PollUiBuilder from "discourse/plugins/poll/discourse/components/modal/poll-ui-builder";
async function setupBuilder(context) { async function setupBuilder() {
const noop = () => {};
const results = []; const results = [];
const model = { const model = {
toolbarEvent: { getText: () => "", addText: (t) => results.push(t) }, toolbarEvent: { getText: () => "", addText: (t) => results.push(t) },
}; };
context.model = model;
await render( await render(
hbs`<Modal::PollUiBuilder @inline={{true}} @model={{this.model}} @closeModal={{fn (mut this.closeCalled) true}} />` <template>
<PollUiBuilder @inline={{true}} @model={{model}} @closeModal={{noop}} />
</template>
); );
return results; return results;
} }
@ -20,7 +24,7 @@ module("Poll | Component | poll-ui-builder", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
test("Can switch poll type", async function (assert) { test("Can switch poll type", async function (assert) {
await setupBuilder(this); await setupBuilder();
assert.dom(".poll-type-value-regular").hasClass("active"); assert.dom(".poll-type-value-regular").hasClass("active");
@ -45,7 +49,7 @@ module("Poll | Component | poll-ui-builder", function (hooks) {
}); });
test("Automatically updates min/max when number of options change", async function (assert) { test("Automatically updates min/max when number of options change", async function (assert) {
await setupBuilder(this); await setupBuilder();
await click(".poll-type-value-multiple"); await click(".poll-type-value-multiple");
assert.dom(".poll-options-min").hasValue("0"); assert.dom(".poll-options-min").hasValue("0");
@ -65,7 +69,7 @@ module("Poll | Component | poll-ui-builder", function (hooks) {
test("disables save button", async function (assert) { test("disables save button", async function (assert) {
this.siteSettings.poll_maximum_options = 3; this.siteSettings.poll_maximum_options = 3;
await setupBuilder(this); await setupBuilder();
assert assert
.dom(".insert-poll") .dom(".insert-poll")
.isDisabled("Insert button disabled when no options specified"); .isDisabled("Insert button disabled when no options specified");
@ -88,7 +92,7 @@ module("Poll | Component | poll-ui-builder", function (hooks) {
}); });
test("number mode", async function (assert) { test("number mode", async function (assert) {
const results = await setupBuilder(this); const results = await setupBuilder();
await click(".show-advanced"); await click(".show-advanced");
await click(".poll-type-value-number"); await click(".poll-type-value-number");
@ -122,7 +126,7 @@ module("Poll | Component | poll-ui-builder", function (hooks) {
}); });
test("regular mode", async function (assert) { test("regular mode", async function (assert) {
const results = await setupBuilder(this); const results = await setupBuilder();
await fillIn(".poll-option-value input", "a"); await fillIn(".poll-option-value input", "a");
await click(".poll-option-add"); await click(".poll-option-add");
@ -160,7 +164,7 @@ module("Poll | Component | poll-ui-builder", function (hooks) {
}); });
test("multi-choice mode", async function (assert) { test("multi-choice mode", async function (assert) {
const results = await setupBuilder(this); const results = await setupBuilder();
await click(".poll-type-value-multiple"); await click(".poll-type-value-multiple");
@ -188,7 +192,7 @@ module("Poll | Component | poll-ui-builder", function (hooks) {
}); });
test("staff_only option is not present for non-staff", async function (assert) { test("staff_only option is not present for non-staff", async function (assert) {
await setupBuilder(this); await setupBuilder();
await click(".show-advanced"); await click(".show-advanced");
const resultVisibility = selectKit(".poll-result"); const resultVisibility = selectKit(".poll-result");
@ -215,7 +219,7 @@ module("Poll | Component | poll-ui-builder", function (hooks) {
test("default public value can be controlled with site setting", async function (assert) { test("default public value can be controlled with site setting", async function (assert) {
this.siteSettings.poll_default_public = false; this.siteSettings.poll_default_public = false;
const results = await setupBuilder(this); const results = await setupBuilder();
await fillIn(".poll-option-value input", "a"); await fillIn(".poll-option-value input", "a");
await click(".poll-option-add"); await click(".poll-option-add");

View File

@ -1,4 +1,7 @@
<section class="color-example"> const ColorExample = <template>
<div class="color-bg {{@color}}"></div> <section class="color-example">
<div class="color-name">var(--{{@color}})</div> <div class="color-bg {{@color}}"></div>
</section> <div class="color-name">var(--{{@color}})</div>
</section>
</template>;
export default ColorExample;

View File

@ -1,114 +1,121 @@
<div class="section-description"> import { i18n } from "discourse-i18n";
<p> import StyleguideExample from "discourse/plugins/styleguide/discourse/components/styleguide-example";
Discourse users can select from 4 different text sizes in their user
settings, by default these are: const FontScale = <template>
<pre> <div class="section-description">
<p>
Discourse users can select from 4 different text sizes in their user
settings, by default these are:
<pre>
Smaller: 14px Normal: 15px Smaller: 14px Normal: 15px
<span <span
>(default)</span> >(default)</span>
Larger: 17px Largest: 19px Larger: 17px Largest: 19px
</pre> </pre>
</p> </p>
<p> <p>
If you'd like to increase the font size of your entire Discourse community, If you'd like to increase the font size of your entire Discourse
you can override the font-size of the HTML element. You can also provide community, you can override the font-size of the HTML element. You can
different font sizes for the user text size settings defined above. The also provide different font sizes for the user text size settings defined
example below increases all text size options by 1px. above. The example below increases all text size options by 1px.
<pre> <pre>
html { html {
<span <span
class="hljs-attribute" class="hljs-attribute"
>font-size</span>: 16px; >font-size</span>: 16px;
<span>// default font-size <span>// default font-size
</span> </span>
&.text-size-smaller { &.text-size-smaller {
<span <span
class="hljs-attribute" class="hljs-attribute"
>font-size</span>: 15px; } &.text-size-larger >font-size</span>: 15px; } &.text-size-larger
{ {
<span <span
class="hljs-attribute" class="hljs-attribute"
>font-size</span>: 18px; } &.text-size-largest >font-size</span>: 18px; } &.text-size-largest
{ {
<span <span
class="hljs-attribute" class="hljs-attribute"
>font-size</span>: 20px; } } >font-size</span>: 20px; } }
</pre> </pre>
</p>
<p>
If you want to scale the fonts of a specific element, you can use
Discourse's font scaling variables. Using the variable system ensures you're
using a consistent set of font-sizes throughout your community.
<p>
Changing the font-size of a parent element will proportionately scale the
font sizes of all its children.
</p> </p>
<pre> <p>
If you want to scale the fonts of a specific element, you can use
Discourse's font scaling variables. Using the variable system ensures
you're using a consistent set of font-sizes throughout your community.
<p>
Changing the font-size of a parent element will proportionately scale
the font sizes of all its children.
</p>
<pre>
.parent { .parent {
<span <span
class="hljs-attribute" class="hljs-attribute"
>font-size</span>: var(--font-up-3); >font-size</span>: var(--font-up-3);
<span>// Increases the relative <span>// Increases the
font-size of this element and its children by 3 steps in the scale</span> relative font-size of this element and its children by 3 steps in the
scale</span>
.child { .child {
<span <span>// If this is set to
>// If this is set to var(--font-down-3) in Discourse's default CSS, the var(--font-down-3) in Discourse's default CSS, the parent font-size
parent font-size increase above would make this equivalent to increase above would make this equivalent to var(--font-0)
var(--font-0) (var(--font-down-3) + var(--font-up-3) = var(--font-0))</span> (var(--font-down-3) + var(--font-up-3) = var(--font-0))</span>
} } } }
</pre> </pre>
</p> </p>
</div> </div>
<StyleguideExample @title="var(--font-up-6), 2.296em"> <StyleguideExample @title="var(--font-up-6), 2.296em">
<p class="font-up-6">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-up-6">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-up-5), 2em"> <StyleguideExample @title="var(--font-up-5), 2em">
<p class="font-up-5">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-up-5">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-up-4), 1.7511em"> <StyleguideExample @title="var(--font-up-4), 1.7511em">
<p class="font-up-4">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-up-4">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-up-3), 1.5157em"> <StyleguideExample @title="var(--font-up-3), 1.5157em">
<p class="font-up-3">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-up-3">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-up-2), 1.3195em"> <StyleguideExample @title="var(--font-up-2), 1.3195em">
<p class="font-up-2">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-up-2">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-up-1), 1.1487em"> <StyleguideExample @title="var(--font-up-1), 1.1487em">
<p class="font-up-1">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-up-1">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-0), 1em — base font"> <StyleguideExample @title="var(--font-0), 1em — base font">
<p class="font-0">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-0">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-down-1), 0.8706em"> <StyleguideExample @title="var(--font-down-1), 0.8706em">
<p class="font-down-1">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-down-1">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-down-2), 0.7579em"> <StyleguideExample @title="var(--font-down-2), 0.7579em">
<p class="font-down-2">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-down-2">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-down-3), 0.6599em"> <StyleguideExample @title="var(--font-down-3), 0.6599em">
<p class="font-down-3">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-down-3">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-down-4), 0.5745em"> <StyleguideExample @title="var(--font-down-4), 0.5745em">
<p class="font-down-4">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-down-4">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-down-5), 0.5em"> <StyleguideExample @title="var(--font-down-5), 0.5em">
<p class="font-down-5">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-down-5">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="var(--font-down-6), 0.4355em"> <StyleguideExample @title="var(--font-down-6), 0.4355em">
<p class="font-down-6">{{i18n "styleguide.sections.typography.example"}}</p> <p class="font-down-6">{{i18n "styleguide.sections.typography.example"}}</p>
</StyleguideExample> </StyleguideExample>
</template>;
export default FontScale;

View File

@ -1,175 +1,187 @@
<StyleguideExample @title=".btn-icon - sizes (large, default, small)"> import { fn } from "@ember/helper";
{{#each @dummy.buttonSizes as |bs|}} import { on } from "@ember/modifier";
<DButton import { not } from "truth-helpers";
@icon="xmark" import DButton from "discourse/components/d-button";
@translatedTitle={{bs.text}} import DToggleSwitch from "discourse/components/d-toggle-switch";
@disabled={{bs.disabled}} import FlatButton from "discourse/components/flat-button";
class={{bs.class}} import concatClass from "discourse/helpers/concat-class";
/> import StyleguideExample from "discourse/plugins/styleguide/discourse/components/styleguide-example";
{{/each}}
</StyleguideExample>
<StyleguideExample @title=".btn-icon - states"> const Buttons = <template>
{{#each @dummy.buttonStates as |bs|}} <StyleguideExample @title=".btn-icon - sizes (large, default, small)">
<DButton {{#each @dummy.buttonSizes as |bs|}}
@icon="xmark" <DButton
@translatedTitle={{bs.text}} @icon="xmark"
@disabled={{bs.disabled}} @translatedTitle={{bs.text}}
class={{bs.class}} @disabled={{bs.disabled}}
/> class={{bs.class}}
{{/each}} />
</StyleguideExample> {{/each}}
</StyleguideExample>
<StyleguideExample @title=".btn-text - sizes (large, default, small)"> <StyleguideExample @title=".btn-icon - states">
{{#each @dummy.buttonSizes as |bs|}} {{#each @dummy.buttonStates as |bs|}}
<DButton <DButton
@translatedLabel={{bs.text}} @icon="xmark"
@disabled={{bs.disabled}} @translatedTitle={{bs.text}}
class={{bs.class}} @disabled={{bs.disabled}}
/> class={{bs.class}}
{{/each}} />
</StyleguideExample> {{/each}}
</StyleguideExample>
<StyleguideExample @title=".btn-text - states"> <StyleguideExample @title=".btn-text - sizes (large, default, small)">
{{#each @dummy.buttonStates as |bs|}} {{#each @dummy.buttonSizes as |bs|}}
<DButton <DButton
@translatedLabel={{bs.text}} @translatedLabel={{bs.text}}
@disabled={{bs.disabled}} @disabled={{bs.disabled}}
class={{bs.class}} class={{bs.class}}
/> />
{{/each}} {{/each}}
</StyleguideExample> </StyleguideExample>
<StyleguideExample <StyleguideExample @title=".btn-text - states">
@title=".btn-default .btn-icon-text - sizes (large, default, small)" {{#each @dummy.buttonStates as |bs|}}
> <DButton
{{#each @dummy.buttonSizes as |bs|}} @translatedLabel={{bs.text}}
<DButton @disabled={{bs.disabled}}
@icon="plus" class={{bs.class}}
@translatedLabel={{bs.text}} />
@disabled={{bs.disabled}} {{/each}}
class={{bs.class}} </StyleguideExample>
/>
{{/each}}
</StyleguideExample>
<StyleguideExample @title=".btn-default .btn-icon-text - states"> <StyleguideExample
{{#each @dummy.buttonStates as |bs|}} @title=".btn-default .btn-icon-text - sizes (large, default, small)"
<DButton >
@icon="plus" {{#each @dummy.buttonSizes as |bs|}}
@translatedLabel={{bs.text}} <DButton
@disabled={{bs.disabled}} @icon="plus"
class={{bs.class}} @translatedLabel={{bs.text}}
/> @disabled={{bs.disabled}}
{{/each}} class={{bs.class}}
</StyleguideExample> />
{{/each}}
</StyleguideExample>
<StyleguideExample <StyleguideExample @title=".btn-default .btn-icon-text - states">
@title=".btn-primary .btn-icon-text - sizes (large, default, small)" {{#each @dummy.buttonStates as |bs|}}
> <DButton
{{#each @dummy.buttonSizes as |bs|}} @icon="plus"
<DButton @translatedLabel={{bs.text}}
@icon="plus" @disabled={{bs.disabled}}
@translatedLabel={{bs.text}} class={{bs.class}}
@disabled={{bs.disabled}} />
class={{concat-class "btn-primary" bs.class}} {{/each}}
/> </StyleguideExample>
{{/each}}
</StyleguideExample>
<StyleguideExample @title=".btn-primary .btn-icon-text - states"> <StyleguideExample
{{#each @dummy.buttonStates as |bs|}} @title=".btn-primary .btn-icon-text - sizes (large, default, small)"
<DButton >
@icon="plus" {{#each @dummy.buttonSizes as |bs|}}
@translatedLabel={{bs.text}} <DButton
@disabled={{bs.disabled}} @icon="plus"
class={{concat-class "btn-primary" bs.class}} @translatedLabel={{bs.text}}
/> @disabled={{bs.disabled}}
{{/each}} class={{concatClass "btn-primary" bs.class}}
</StyleguideExample> />
{{/each}}
</StyleguideExample>
<StyleguideExample <StyleguideExample @title=".btn-primary .btn-icon-text - states">
@title=".btn-danger .btn-icon-text - sizes (large, default, small)" {{#each @dummy.buttonStates as |bs|}}
> <DButton
{{#each @dummy.buttonSizes as |bs|}} @icon="plus"
<DButton @translatedLabel={{bs.text}}
@icon="trash-can" @disabled={{bs.disabled}}
@translatedLabel={{bs.text}} class={{concatClass "btn-primary" bs.class}}
@disabled={{bs.disabled}} />
class={{concat-class "btn-danger" bs.class}} {{/each}}
/> </StyleguideExample>
{{/each}}
</StyleguideExample>
<StyleguideExample @title=".btn-danger .btn-icon-text - states"> <StyleguideExample
{{#each @dummy.buttonStates as |bs|}} @title=".btn-danger .btn-icon-text - sizes (large, default, small)"
<DButton >
@icon="trash-can" {{#each @dummy.buttonSizes as |bs|}}
@translatedLabel={{bs.text}} <DButton
@disabled={{bs.disabled}} @icon="trash-can"
class={{concat-class "btn-danger" bs.class}} @translatedLabel={{bs.text}}
/> @disabled={{bs.disabled}}
{{/each}} class={{concatClass "btn-danger" bs.class}}
</StyleguideExample> />
{{/each}}
</StyleguideExample>
<StyleguideExample @title=".btn-flat - sizes (large, default, small)"> <StyleguideExample @title=".btn-danger .btn-icon-text - states">
{{#each @dummy.buttonSizes as |bs|}} {{#each @dummy.buttonStates as |bs|}}
<FlatButton <DButton
@icon="trash-can" @icon="trash-can"
@disabled={{bs.disabled}} @translatedLabel={{bs.text}}
@translatedTitle={{bs.title}} @disabled={{bs.disabled}}
/> class={{concatClass "btn-danger" bs.class}}
{{/each}} />
</StyleguideExample> {{/each}}
</StyleguideExample>
<StyleguideExample @title=".btn-flat - states"> <StyleguideExample @title=".btn-flat - sizes (large, default, small)">
{{#each @dummy.buttonStates as |bs|}} {{#each @dummy.buttonSizes as |bs|}}
<FlatButton <FlatButton
@icon="trash-can" @icon="trash-can"
@disabled={{bs.disabled}} @disabled={{bs.disabled}}
@translatedTitle={{bs.title}} @translatedTitle={{bs.title}}
/> />
{{/each}} {{/each}}
</StyleguideExample> </StyleguideExample>
<StyleguideExample <StyleguideExample @title=".btn-flat - states">
@title="<DButton> btn-flat btn-text - sizes (large, default, small)" {{#each @dummy.buttonStates as |bs|}}
> <FlatButton
{{#each @dummy.buttonSizes as |bs|}} @icon="trash-can"
<DButton @disabled={{bs.disabled}}
@disabled={{bs.disabled}} @translatedTitle={{bs.title}}
@translatedLabel={{bs.text}} />
class={{concat-class "btn-flat" bs.class}} {{/each}}
/> </StyleguideExample>
{{/each}}
</StyleguideExample>
<StyleguideExample @title="<DButton> btn-flat btn-text - states"> <StyleguideExample
{{#each @dummy.buttonStates as |bs|}} @title="<DButton> btn-flat btn-text - sizes (large, default, small)"
<DButton >
@disabled={{bs.disabled}} {{#each @dummy.buttonSizes as |bs|}}
@translatedLabel={{bs.text}} <DButton
class={{concat-class "btn-flat" bs.class}} @disabled={{bs.disabled}}
/> @translatedLabel={{bs.text}}
{{/each}} class={{concatClass "btn-flat" bs.class}}
</StyleguideExample> />
{{/each}}
</StyleguideExample>
<StyleguideExample @title="<DToggleSwitch>"> <StyleguideExample @title="<DButton> btn-flat btn-text - states">
<DToggleSwitch {{#each @dummy.buttonStates as |bs|}}
@state={{@dummy.toggleSwitchState}} <DButton
{{on @disabled={{bs.disabled}}
"click" @translatedLabel={{bs.text}}
(fn (mut @dummy.toggleSwitchState) (not @dummy.toggleSwitchState)) class={{concatClass "btn-flat" bs.class}}
}} />
/> {{/each}}
<DToggleSwitch </StyleguideExample>
disabled="true"
@state={{true}} <StyleguideExample @title="<DToggleSwitch>">
title="Disabled with state=true" <DToggleSwitch
/> @state={{@dummy.toggleSwitchState}}
<DToggleSwitch {{on
disabled="true" "click"
@state={{false}} (fn (mut @dummy.toggleSwitchState) (not @dummy.toggleSwitchState))
title="Disabled with state=false" }}
/> />
</StyleguideExample> <DToggleSwitch
disabled="true"
@state={{true}}
title="Disabled with state=true"
/>
<DToggleSwitch
disabled="true"
@state={{false}}
title="Disabled with state=false"
/>
</StyleguideExample>
</template>;
export default Buttons;

View File

@ -1,120 +1,126 @@
<StyleguideExample @title="primary"> import ColorExample from "discourse/plugins/styleguide/discourse/components/color-example";
<section class="color-row"> import StyleguideExample from "discourse/plugins/styleguide/discourse/components/styleguide-example";
<ColorExample @color="primary-very-low" />
<ColorExample @color="primary-low" />
<ColorExample @color="primary-low-mid" />
</section>
<section class="color-row">
<ColorExample @color="primary-medium" />
<ColorExample @color="primary-high" />
<ColorExample @color="primary" />
</section>
</StyleguideExample>
<StyleguideExample @title="primary-100"> const Colors = <template>
<section class="color-row"> <StyleguideExample @title="primary">
<ColorExample @color="primary-50" /> <section class="color-row">
<ColorExample @color="primary-100" /> <ColorExample @color="primary-very-low" />
<ColorExample @color="primary-200" /> <ColorExample @color="primary-low" />
<ColorExample @color="primary-300" /> <ColorExample @color="primary-low-mid" />
<ColorExample @color="primary-400" /> </section>
<ColorExample @color="primary-500" /> <section class="color-row">
</section> <ColorExample @color="primary-medium" />
<section class="color-row"> <ColorExample @color="primary-high" />
<ColorExample @color="primary-600" /> <ColorExample @color="primary" />
<ColorExample @color="primary-700" /> </section>
<ColorExample @color="primary-800" /> </StyleguideExample>
<ColorExample @color="primary-900" />
<ColorExample @color="primary" />
</section>
</StyleguideExample>
<StyleguideExample @title="secondary"> <StyleguideExample @title="primary-100">
<section class="color-row"> <section class="color-row">
<ColorExample @color="secondary-low" /> <ColorExample @color="primary-50" />
<ColorExample @color="secondary-medium" /> <ColorExample @color="primary-100" />
<ColorExample @color="secondary-high" /> <ColorExample @color="primary-200" />
<ColorExample @color="secondary" /> <ColorExample @color="primary-300" />
</section> <ColorExample @color="primary-400" />
</StyleguideExample> <ColorExample @color="primary-500" />
</section>
<section class="color-row">
<ColorExample @color="primary-600" />
<ColorExample @color="primary-700" />
<ColorExample @color="primary-800" />
<ColorExample @color="primary-900" />
<ColorExample @color="primary" />
</section>
</StyleguideExample>
<StyleguideExample @title="tertiary"> <StyleguideExample @title="secondary">
<section class="color-row"> <section class="color-row">
<ColorExample @color="tertiary-low" /> <ColorExample @color="secondary-low" />
<ColorExample @color="tertiary-medium" /> <ColorExample @color="secondary-medium" />
<ColorExample @color="tertiary-high" /> <ColorExample @color="secondary-high" />
<ColorExample @color="tertiary" /> <ColorExample @color="secondary" />
</section> </section>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="tertiary-100"> <StyleguideExample @title="tertiary">
<section class="color-row"> <section class="color-row">
<ColorExample @color="tertiary-50" /> <ColorExample @color="tertiary-low" />
<ColorExample @color="tertiary-100" /> <ColorExample @color="tertiary-medium" />
<ColorExample @color="tertiary-200" /> <ColorExample @color="tertiary-high" />
<ColorExample @color="tertiary-300" /> <ColorExample @color="tertiary" />
<ColorExample @color="tertiary-400" /> </section>
<ColorExample @color="tertiary-500" /> </StyleguideExample>
</section>
<section class="color-row">
<ColorExample @color="tertiary-600" />
<ColorExample @color="tertiary-700" />
<ColorExample @color="tertiary-800" />
<ColorExample @color="tertiary-900" />
<ColorExample @color="tertiary" />
</section>
</StyleguideExample>
<StyleguideExample @title="quaternary"> <StyleguideExample @title="tertiary-100">
<section class="color-row"> <section class="color-row">
<ColorExample @color="quaternary-low" /> <ColorExample @color="tertiary-50" />
<ColorExample @color="quaternary" /> <ColorExample @color="tertiary-100" />
</section> <ColorExample @color="tertiary-200" />
</StyleguideExample> <ColorExample @color="tertiary-300" />
<ColorExample @color="tertiary-400" />
<ColorExample @color="tertiary-500" />
</section>
<section class="color-row">
<ColorExample @color="tertiary-600" />
<ColorExample @color="tertiary-700" />
<ColorExample @color="tertiary-800" />
<ColorExample @color="tertiary-900" />
<ColorExample @color="tertiary" />
</section>
</StyleguideExample>
<StyleguideExample @title="highlight"> <StyleguideExample @title="quaternary">
<section class="color-row"> <section class="color-row">
<ColorExample @color="highlight-bg" /> <ColorExample @color="quaternary-low" />
<ColorExample @color="highlight" /> <ColorExample @color="quaternary" />
</section> </section>
</StyleguideExample> </StyleguideExample>
<StyleguideExample @title="danger"> <StyleguideExample @title="highlight">
<section class="color-row"> <section class="color-row">
<ColorExample @color="danger-low" /> <ColorExample @color="highlight-bg" />
<ColorExample @color="danger-low-mid" /> <ColorExample @color="highlight" />
<ColorExample @color="danger-medium" /> </section>
<ColorExample @color="danger" /> </StyleguideExample>
</section>
</StyleguideExample>
<StyleguideExample @title="success"> <StyleguideExample @title="danger">
<section class="color-row"> <section class="color-row">
<ColorExample @color="success-low" /> <ColorExample @color="danger-low" />
<ColorExample @color="success-medium" /> <ColorExample @color="danger-low-mid" />
<ColorExample @color="success" /> <ColorExample @color="danger-medium" />
</section> <ColorExample @color="danger" />
</StyleguideExample> </section>
</StyleguideExample>
<StyleguideExample @title="love"> <StyleguideExample @title="success">
<section class="color-row"> <section class="color-row">
<ColorExample @color="love-low" /> <ColorExample @color="success-low" />
<ColorExample @color="love" /> <ColorExample @color="success-medium" />
</section> <ColorExample @color="success" />
</StyleguideExample> </section>
</StyleguideExample>
<StyleguideExample @title="header_primary"> <StyleguideExample @title="love">
<section class="color-row"> <section class="color-row">
<ColorExample @color="header_background" /> <ColorExample @color="love-low" />
</section> <ColorExample @color="love" />
<section class="color-row"> </section>
<ColorExample @color="header_primary" /> </StyleguideExample>
<ColorExample @color="header_primary-very-high" />
<ColorExample @color="header_primary-high" /> <StyleguideExample @title="header_primary">
</section> <section class="color-row">
<section class="color-row"> <ColorExample @color="header_background" />
<ColorExample @color="header_primary-medium" /> </section>
<ColorExample @color="header_primary-low-mid" /> <section class="color-row">
<ColorExample @color="header_primary-low" /> <ColorExample @color="header_primary" />
</section> <ColorExample @color="header_primary-very-high" />
</StyleguideExample> <ColorExample @color="header_primary-high" />
</section>
<section class="color-row">
<ColorExample @color="header_primary-medium" />
<ColorExample @color="header_primary-low-mid" />
<ColorExample @color="header_primary-low" />
</section>
</StyleguideExample>
</template>;
export default Colors;

View File

@ -1,25 +1,32 @@
<div class="section-description"> import { i18n } from "discourse-i18n";
<p>Discourse uses a free set of SVG icons from Font Awesome (<a import StyleguideExample from "discourse/plugins/styleguide/discourse/components/styleguide-example";
href="https://fontawesome.com/icons?d=gallery&m=free" import StyleguideIcons from "discourse/plugins/styleguide/discourse/components/styleguide-icons";
>{{i18n "styleguide.sections.icons.full_list"}}</a>).</p>
<p>Plugins and themes can add SVG icons to the SVG spritesheet, or replace
existing icons entirely.</p>
<p>
<ul>
<li><a
href="https://meta.discourse.org/t/introducing-font-awesome-5-and-svg-icons/101643"
>How to use SVG icons in your plugin or theme</a></li>
<li><a
href="https://meta.discourse.org/t/replace-discourses-default-svg-icons-with-custom-icons-in-a-theme/115736/1"
>How to replace Discourse's default icons in a theme</a></li>
</ul>
</p>
<p>By default, all icons have the
<pre class="pre-inline">.d-icon</pre>
class applied along with a class containing the name of the icon (e.g.,
<pre class="pre-inline">.d-icon-link</pre>)</p>
</div>
<StyleguideExample @title="d-icon - all available icons"> const Icons = <template>
<StyleguideIcons /> <div class="section-description">
</StyleguideExample> <p>Discourse uses a free set of SVG icons from Font Awesome (<a
href="https://fontawesome.com/icons?d=gallery&m=free"
>{{i18n "styleguide.sections.icons.full_list"}}</a>).</p>
<p>Plugins and themes can add SVG icons to the SVG spritesheet, or replace
existing icons entirely.</p>
<p>
<ul>
<li><a
href="https://meta.discourse.org/t/introducing-font-awesome-5-and-svg-icons/101643"
>How to use SVG icons in your plugin or theme</a></li>
<li><a
href="https://meta.discourse.org/t/replace-discourses-default-svg-icons-with-custom-icons-in-a-theme/115736/1"
>How to replace Discourse's default icons in a theme</a></li>
</ul>
</p>
<p>By default, all icons have the
<pre class="pre-inline">.d-icon</pre>
class applied along with a class containing the name of the icon (e.g.,
<pre class="pre-inline">.d-icon-link</pre>)</p>
</div>
<StyleguideExample @title="d-icon - all available icons">
<StyleguideIcons />
</StyleguideExample>
</template>;
export default Icons;

View File

@ -1,274 +1,284 @@
<h2>Controls</h2> import { array, fn, hash } from "@ember/helper";
<StyleguideExample @title="Input"> import Form from "discourse/components/form";
<Form as |form|> import StyleguideExample from "discourse/plugins/styleguide/discourse/components/styleguide-example";
<form.Field @title="Username" @name="username" as |field|>
<field.Input placeholder="Username" /> const Forms = <template>
</form.Field> <h2>Controls</h2>
<form.Field @title="Age" @name="age" as |field|> <StyleguideExample @title="Input">
<field.Input placeholder="Age" @type="number" @format="small" /> <Form as |form|>
</form.Field> <form.Field @title="Username" @name="username" as |field|>
<form.Field @title="Website" @name="website" as |field|> <field.Input placeholder="Username" />
<field.Input @before="https://" @after=".com" @format="large" /> </form.Field>
</form.Field> <form.Field @title="Age" @name="age" as |field|>
<form.Field @title="After" @name="after" as |field|> <field.Input placeholder="Age" @type="number" @format="small" />
<field.Input @after=".com" /> </form.Field>
</form.Field> <form.Field @title="Website" @name="website" as |field|>
<form.Field @title="Before" @name="before" as |field|> <field.Input @before="https://" @after=".com" @format="large" />
<field.Input @before="https://" /> </form.Field>
</form.Field> <form.Field @title="After" @name="after" as |field|>
<form.Field <field.Input @after=".com" />
@title="Secret" </form.Field>
@name="secret" <form.Field @title="Before" @name="before" as |field|>
@description="An important password" <field.Input @before="https://" />
as |field| </form.Field>
<form.Field
@title="Secret"
@name="secret"
@description="An important password"
as |field|
>
<field.Password />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Question">
<Form as |form|>
<form.Field @title="Enabled" @name="enabled" as |field|>
<field.Question />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Toggle">
<Form as |form|>
<form.Field @title="Enabled" @name="enabled" as |field|>
<field.Toggle />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Composer">
<Form as |form|>
<form.Field @title="Query" @name="query" as |field|>
<field.Composer />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Code">
<Form as |form|>
<form.Field @title="Query" @name="query" as |field|>
<field.Code />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Textarea">
<Form as |form|>
<form.Field @title="Query" @name="query" as |field|>
<field.Textarea />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Select">
<Form as |form|>
<form.Field @title="Enabled" @name="enabled" as |field|>
<field.Select as |select|>
<select.Option @value="true">Yes</select.Option>
<select.Option @value="false">No</select.Option>
</field.Select>
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="CheckboxGroup">
<Form as |form|>
<form.CheckboxGroup
@title="I give explicit permission"
as |checkboxGroup|
>
<checkboxGroup.Field
@title="Use my email for any purpose."
@name="contract"
as |field|
>
<field.Checkbox>Including signing up for services I can't unsubscribe
to.</field.Checkbox>
</checkboxGroup.Field>
<checkboxGroup.Field
@title="Sign my soul away."
@name="contract2"
as |field|
>
<field.Checkbox>Will severly impact the afterlife experience.</field.Checkbox>
</checkboxGroup.Field>
</form.CheckboxGroup>
</Form>
</StyleguideExample>
<StyleguideExample @title="Image">
<Form as |form|>
<form.Field @title="Image" @name="image" as |field|>
<field.Image @type="avatar" />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Icon">
<Form as |form|>
<form.Field @title="Icon" @name="icon" as |field|>
<field.Icon />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Menu">
<Form as |form data|>
<form.Field @title="Enabled" @name="enabled" as |field|>
<field.Menu @selection={{data.enabled}} as |menu|>
<menu.Item @value="true">Yes</menu.Item>
<menu.Divider />
<menu.Item @value="false">No</menu.Item>
</field.Menu>
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="RadioGroup">
<Form as |form|>
<form.Field @title="Enabled" @name="enabled" @format="full" as |field|>
<field.RadioGroup as |radioGroup|>
<radioGroup.Radio @value="true">Yes</radioGroup.Radio>
<radioGroup.Radio @value="false" as |radio|>
<radio.Title>No</radio.Title>
<radio.Description>
Choosing no, will make you inelligible for the contest.
</radio.Description>
</radioGroup.Radio>
</field.RadioGroup>
</form.Field>
</Form>
</StyleguideExample>
<h2>Layout</h2>
<StyleguideExample @title="Section">
<Form as |form|>
<form.Section @title="Section title">
Content
</form.Section>
</Form>
</StyleguideExample>
<StyleguideExample @title="Alert">
<Form as |form|>
<form.Alert @icon="pencil">
You can edit this form.
</form.Alert>
</Form>
</StyleguideExample>
<StyleguideExample @title="InputGroup">
<Form as |form|>
<form.InputGroup as |inputGroup|>
<inputGroup.Field @title="Username" @name="username" as |field|>
<field.Input />
</inputGroup.Field>
<inputGroup.Field @title="Email" @name="email" as |field|>
<field.Input />
</inputGroup.Field>
</form.InputGroup>
</Form>
</StyleguideExample>
<StyleguideExample @title="Collection">
<Form
@data={{hash foo=(array (hash bar=1 baz=2) (hash bar=3 baz=4))}}
as |form|
> >
<field.Password /> <form.Button @action={{fn form.addItemToCollection "foo"}} @icon="plus" />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Question"> <form.Collection @name="foo" as |collection index|>
<Form as |form|> <form.Row as |row|>
<form.Field @title="Enabled" @name="enabled" as |field|> <row.Col @size={{6}}>
<field.Question /> <collection.Field @title="Bar" @name="bar" as |field|>
</form.Field> <field.Input />
</Form> </collection.Field>
</StyleguideExample> </row.Col>
<StyleguideExample @title="Toggle"> <row.Col @size={{4}}>
<Form as |form|> <collection.Field @title="Baz" @name="baz" as |field|>
<form.Field @title="Enabled" @name="enabled" as |field|> <field.Input />
<field.Toggle /> </collection.Field>
</form.Field> </row.Col>
</Form>
</StyleguideExample>
<StyleguideExample @title="Composer"> <row.Col @size={{2}}>
<Form as |form|> <form.Button @action={{fn collection.remove index}} @icon="minus" />
<form.Field @title="Query" @name="query" as |field|> </row.Col>
<field.Composer /> </form.Row>
</form.Field> </form.Collection>
</Form>
</StyleguideExample>
<StyleguideExample @title="Code"> </Form>
<Form as |form|> </StyleguideExample>
<form.Field @title="Query" @name="query" as |field|>
<field.Code />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Textarea"> <StyleguideExample @title="Row/Col">
<Form as |form|> <Form as |form|>
<form.Field @title="Query" @name="query" as |field|>
<field.Textarea />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Select">
<Form as |form|>
<form.Field @title="Enabled" @name="enabled" as |field|>
<field.Select as |select|>
<select.Option @value="true">Yes</select.Option>
<select.Option @value="false">No</select.Option>
</field.Select>
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="CheckboxGroup">
<Form as |form|>
<form.CheckboxGroup @title="I give explicit permission" as |checkboxGroup|>
<checkboxGroup.Field
@title="Use my email for any purpose."
@name="contract"
as |field|
>
<field.Checkbox>Including signing up for services I can't unsubscribe
to.</field.Checkbox>
</checkboxGroup.Field>
<checkboxGroup.Field
@title="Sign my soul away."
@name="contract2"
as |field|
>
<field.Checkbox>Will severly impact the afterlife experience.</field.Checkbox>
</checkboxGroup.Field>
</form.CheckboxGroup>
</Form>
</StyleguideExample>
<StyleguideExample @title="Image">
<Form as |form|>
<form.Field @title="Image" @name="image" as |field|>
<field.Image @type="avatar" />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Icon">
<Form as |form|>
<form.Field @title="Icon" @name="icon" as |field|>
<field.Icon />
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="Menu">
<Form as |form data|>
<form.Field @title="Enabled" @name="enabled" as |field|>
<field.Menu @selection={{data.enabled}} as |menu|>
<menu.Item @value="true">Yes</menu.Item>
<menu.Divider />
<menu.Item @value="false">No</menu.Item>
</field.Menu>
</form.Field>
</Form>
</StyleguideExample>
<StyleguideExample @title="RadioGroup">
<Form as |form|>
<form.Field @title="Enabled" @name="enabled" @format="full" as |field|>
<field.RadioGroup as |radioGroup|>
<radioGroup.Radio @value="true">Yes</radioGroup.Radio>
<radioGroup.Radio @value="false" as |radio|>
<radio.Title>No</radio.Title>
<radio.Description>
Choosing no, will make you inelligible for the contest.
</radio.Description>
</radioGroup.Radio>
</field.RadioGroup>
</form.Field>
</Form>
</StyleguideExample>
<h2>Layout</h2>
<StyleguideExample @title="Section">
<Form as |form|>
<form.Section @title="Section title">
Content
</form.Section>
</Form>
</StyleguideExample>
<StyleguideExample @title="Alert">
<Form as |form|>
<form.Alert @icon="pencil">
You can edit this form.
</form.Alert>
</Form>
</StyleguideExample>
<StyleguideExample @title="InputGroup">
<Form as |form|>
<form.InputGroup as |inputGroup|>
<inputGroup.Field @title="Username" @name="username" as |field|>
<field.Input />
</inputGroup.Field>
<inputGroup.Field @title="Email" @name="email" as |field|>
<field.Input />
</inputGroup.Field>
</form.InputGroup>
</Form>
</StyleguideExample>
<StyleguideExample @title="Collection">
<Form
@data={{hash foo=(array (hash bar=1 baz=2) (hash bar=3 baz=4))}}
as |form|
>
<form.Button @action={{fn form.addItemToCollection "foo"}} @icon="plus" />
<form.Collection @name="foo" as |collection index|>
<form.Row as |row|> <form.Row as |row|>
<row.Col @size={{6}}> <row.Col @size={{6}}>
<collection.Field @title="Bar" @name="bar" as |field|> <form.Field
@title="Username"
@name="username"
@validation="required"
as |field|
>
<field.Input /> <field.Input />
</collection.Field> </form.Field>
</row.Col> </row.Col>
<row.Col @size={{4}}> <row.Col @size={{4}}>
<collection.Field @title="Baz" @name="baz" as |field|> <form.Field @title="Email" @name="email" as |field|>
<field.Input /> <field.Input />
</collection.Field> </form.Field>
</row.Col> </row.Col>
<row.Col @size={{2}}> <row.Col @size={{2}}>
<form.Button @action={{fn collection.remove index}} @icon="minus" /> <form.Submit />
</row.Col> </row.Col>
</form.Row> </form.Row>
</form.Collection> </Form>
</StyleguideExample>
</Form> <StyleguideExample @title="Multiline">
</StyleguideExample> <Form as |form|>
<form.Row as |row|>
<row.Col @size={{6}}>
<form.Field
@title="Username"
@name="username"
@validation="required"
as |field|
>
<field.Input />
</form.Field>
</row.Col>
<row.Col @size={{6}}>
<form.Field @title="Email" @name="email" as |field|>
<field.Input />
</form.Field>
</row.Col>
<StyleguideExample @title="Row/Col"> <row.Col @size={{12}}>
<Form as |form|> <form.Field @title="Adress" @name="adress" as |field|>
<form.Row as |row|> <field.Input />
<row.Col @size={{6}}> </form.Field>
<form.Field </row.Col>
@title="Username" </form.Row>
@name="username" </Form>
@validation="required" </StyleguideExample>
as |field|
>
<field.Input />
</form.Field>
</row.Col>
<row.Col @size={{4}}>
<form.Field @title="Email" @name="email" as |field|>
<field.Input />
</form.Field>
</row.Col>
<row.Col @size={{2}}>
<form.Submit />
</row.Col>
</form.Row>
</Form>
</StyleguideExample>
<StyleguideExample @title="Multiline"> <h2>Validation</h2>
<Form as |form|>
<form.Row as |row|>
<row.Col @size={{6}}>
<form.Field
@title="Username"
@name="username"
@validation="required"
as |field|
>
<field.Input />
</form.Field>
</row.Col>
<row.Col @size={{6}}>
<form.Field @title="Email" @name="email" as |field|>
<field.Input />
</form.Field>
</row.Col>
<row.Col @size={{12}}> <StyleguideExample @title="Input">
<form.Field @title="Adress" @name="adress" as |field|> <Form @validateOn="change" as |form|>
<field.Input /> <form.Field
</form.Field> @title="Username"
</row.Col> @name="username"
</form.Row> @validation="required"
</Form> as |field|
</StyleguideExample> >
<field.Input />
<h2>Validation</h2> </form.Field>
</Form>
<StyleguideExample @title="Input"> </StyleguideExample>
<Form @validateOn="change" as |form|> </template>;
<form.Field export default Forms;
@title="Username"
@name="username"
@validation="required"
as |field|
>
<field.Input />
</form.Field>
</Form>
</StyleguideExample>

View File

@ -1,7 +1,12 @@
<StyleguideExample @title="spinner - small"> import StyleguideExample from "discourse/plugins/styleguide/discourse/components/styleguide-example";
<div class="spinner small"></div>
</StyleguideExample>
<StyleguideExample @title="spinner - regular"> const Spinners = <template>
<div class="spinner"></div> <StyleguideExample @title="spinner - small">
</StyleguideExample> <div class="spinner small"></div>
</StyleguideExample>
<StyleguideExample @title="spinner - regular">
<div class="spinner"></div>
</StyleguideExample>
</template>;
export default Spinners;

Some files were not shown because too many files have changed in this diff Show More