mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 15:25:35 +08:00
UX: move admin flag form to form-kit (#28187)
Rewrite the admin flag form to use FormKit. This is a draft because waiting for Checkbox improvements.
This commit is contained in:
parent
2b577950af
commit
300ef67481
|
@ -1,18 +1,13 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { TextArea } from "@ember/legacy-built-in-components";
|
||||
import { on } from "@ember/modifier";
|
||||
import { cached } from "@glimmer/tracking";
|
||||
import { hash } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import { LinkTo } from "@ember/routing";
|
||||
import { service } from "@ember/service";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { not } from "truth-helpers";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import withEventValue from "discourse/helpers/with-event-value";
|
||||
import Form from "discourse/components/form";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import dIcon from "discourse-common/helpers/d-icon";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import i18n from "discourse-common/helpers/i18n";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import I18n from "discourse-i18n";
|
||||
|
@ -23,33 +18,26 @@ export default class AdminFlagsForm extends Component {
|
|||
@service router;
|
||||
@service site;
|
||||
|
||||
@tracked enabled = true;
|
||||
@tracked requireMessage = false;
|
||||
@tracked name;
|
||||
@tracked description;
|
||||
@tracked appliesTo;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
if (this.isUpdate) {
|
||||
this.name = this.args.flag.name;
|
||||
this.description = this.args.flag.description;
|
||||
this.appliesTo = this.args.flag.applies_to;
|
||||
this.requireMessage = this.args.flag.require_message;
|
||||
this.enabled = this.args.flag.enabled;
|
||||
}
|
||||
}
|
||||
|
||||
get isUpdate() {
|
||||
return this.args.flag;
|
||||
}
|
||||
|
||||
get isValid() {
|
||||
return (
|
||||
!isEmpty(this.name) &&
|
||||
!isEmpty(this.description) &&
|
||||
!isEmpty(this.appliesTo)
|
||||
);
|
||||
@cached
|
||||
get formData() {
|
||||
if (this.isUpdate) {
|
||||
return {
|
||||
name: this.args.flag.name,
|
||||
description: this.args.flag.description,
|
||||
appliesTo: this.args.flag.applies_to,
|
||||
requireMessage: this.args.flag.require_message,
|
||||
enabled: this.args.flag.enabled,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
enabled: true,
|
||||
requireMessage: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
get header() {
|
||||
|
@ -71,64 +59,59 @@ export default class AdminFlagsForm extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
@action
|
||||
save() {
|
||||
this.isUpdate ? this.update() : this.create();
|
||||
}
|
||||
|
||||
@action
|
||||
onToggleRequireMessage(e) {
|
||||
this.requireMessage = e.target.checked;
|
||||
}
|
||||
|
||||
@action
|
||||
onToggleEnabled(e) {
|
||||
this.enabled = e.target.checked;
|
||||
}
|
||||
|
||||
@bind
|
||||
create() {
|
||||
return ajax(`/admin/config/flags`, {
|
||||
type: "POST",
|
||||
data: this.#formData,
|
||||
})
|
||||
.then((response) => {
|
||||
this.site.flagTypes.push(response.flag);
|
||||
this.router.transitionTo("adminConfig.flags");
|
||||
})
|
||||
.catch((error) => {
|
||||
return popupAjaxError(error);
|
||||
validateAppliesTo(name, value, { addError }) {
|
||||
if (value && value.length === 0) {
|
||||
addError("appliesTo", {
|
||||
title: i18n("admin.config_areas.flags.form.applies_to"),
|
||||
message: i18n("admin.config_areas.flags.form.invalid_applies_to"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
update() {
|
||||
return ajax(`/admin/config/flags/${this.args.flag.id}`, {
|
||||
type: "PUT",
|
||||
data: this.#formData,
|
||||
})
|
||||
.then((response) => {
|
||||
this.args.flag.name = response.flag.name;
|
||||
this.args.flag.description = response.flag.description;
|
||||
this.args.flag.applies_to = response.flag.applies_to;
|
||||
this.args.flag.require_message = response.flag.require_message;
|
||||
this.args.flag.enabled = response.flag.enabled;
|
||||
this.router.transitionTo("adminConfig.flags");
|
||||
})
|
||||
.catch((error) => {
|
||||
return popupAjaxError(error);
|
||||
});
|
||||
}
|
||||
|
||||
@bind
|
||||
get #formData() {
|
||||
return {
|
||||
name: this.name,
|
||||
description: this.description,
|
||||
applies_to: this.appliesTo,
|
||||
require_message: this.requireMessage,
|
||||
enabled: this.enabled,
|
||||
@action
|
||||
save({ name, description, appliesTo, requireMessage, enabled }) {
|
||||
const createOrUpdate = this.isUpdate ? this.update : this.create;
|
||||
const data = {
|
||||
name,
|
||||
description,
|
||||
enabled,
|
||||
applies_to: appliesTo,
|
||||
require_message: requireMessage,
|
||||
};
|
||||
createOrUpdate(data);
|
||||
}
|
||||
|
||||
@bind
|
||||
async create(data) {
|
||||
try {
|
||||
const response = await ajax("/admin/config/flags", {
|
||||
type: "POST",
|
||||
data,
|
||||
});
|
||||
this.site.flagTypes.push(response.flag);
|
||||
this.router.transitionTo("adminConfig.flags");
|
||||
} catch (error) {
|
||||
popupAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@bind
|
||||
async update(data) {
|
||||
try {
|
||||
const response = await ajax(`/admin/config/flags/${this.args.flag.id}`, {
|
||||
type: "PUT",
|
||||
data,
|
||||
});
|
||||
|
||||
this.args.flag.name = response.flag.name;
|
||||
this.args.flag.description = response.flag.description;
|
||||
this.args.flag.applies_to = response.flag.applies_to;
|
||||
this.args.flag.require_message = response.flag.require_message;
|
||||
this.args.flag.enabled = response.flag.enabled;
|
||||
this.router.transitionTo("adminConfig.flags");
|
||||
} catch (error) {
|
||||
popupAjaxError(error);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
|
@ -138,89 +121,78 @@ export default class AdminFlagsForm extends Component {
|
|||
@route="adminConfig.flags"
|
||||
class="btn-default btn btn-icon-text btn-back"
|
||||
>
|
||||
{{dIcon "chevron-left"}}
|
||||
{{icon "chevron-left"}}
|
||||
{{i18n "admin.config_areas.flags.back"}}
|
||||
</LinkTo>
|
||||
<div class="admin-config-area__primary-content admin-flag-form">
|
||||
<AdminConfigAreaCard @heading={{this.header}}>
|
||||
<div class="control-group">
|
||||
<label for="name">
|
||||
{{i18n "admin.config_areas.flags.form.name"}}
|
||||
</label>
|
||||
<input
|
||||
name="name"
|
||||
type="text"
|
||||
value={{this.name}}
|
||||
maxlength="200"
|
||||
class="admin-flag-form__name"
|
||||
{{on "input" (withEventValue (fn (mut this.name)))}}
|
||||
/>
|
||||
</div>
|
||||
<Form @onSubmit={{this.save}} @data={{this.formData}} as |form|>
|
||||
<form.Field
|
||||
@name="name"
|
||||
@title={{i18n "admin.config_areas.flags.form.name"}}
|
||||
@validation="required|length:3,200"
|
||||
@format="large"
|
||||
as |field|
|
||||
>
|
||||
<field.Input />
|
||||
</form.Field>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="description">
|
||||
{{i18n "admin.config_areas.flags.form.description"}}
|
||||
</label>
|
||||
<TextArea
|
||||
@value={{this.description}}
|
||||
maxlength="1000"
|
||||
class="admin-flag-form__description"
|
||||
/>
|
||||
</div>
|
||||
<form.Field
|
||||
@name="description"
|
||||
@title={{i18n "admin.config_areas.flags.form.description"}}
|
||||
@validation="length:0,1000"
|
||||
as |field|
|
||||
>
|
||||
<field.Textarea @height={{60}} />
|
||||
</form.Field>
|
||||
|
||||
<div class="control-group">
|
||||
<label for="applies-to">
|
||||
{{i18n "admin.config_areas.flags.form.applies_to"}}
|
||||
</label>
|
||||
<MultiSelect
|
||||
@value={{this.appliesTo}}
|
||||
@content={{this.appliesToValues}}
|
||||
@options={{hash allowAny=false}}
|
||||
class="admin-flag-form__applies-to"
|
||||
/>
|
||||
</div>
|
||||
<form.Field
|
||||
@name="appliesTo"
|
||||
@title={{i18n "admin.config_areas.flags.form.applies_to"}}
|
||||
@validation="required"
|
||||
@validate={{this.validateAppliesTo}}
|
||||
as |field|
|
||||
>
|
||||
<field.Custom>
|
||||
<MultiSelect
|
||||
@id={{field.id}}
|
||||
@value={{field.value}}
|
||||
@onChange={{field.set}}
|
||||
@content={{this.appliesToValues}}
|
||||
@options={{hash allowAny=false}}
|
||||
class="admin-flag-form__applies-to"
|
||||
/>
|
||||
</field.Custom>
|
||||
</form.Field>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="checkbox-label admin-flag-form__require-reason">
|
||||
<input
|
||||
{{on "input" this.onToggleRequireMessage}}
|
||||
type="checkbox"
|
||||
checked={{this.requireMessage}}
|
||||
/>
|
||||
<div>
|
||||
{{i18n "admin.config_areas.flags.form.require_message"}}
|
||||
<div class="admin-flag-form__require-message-description">
|
||||
<form.CheckboxGroup as |checkboxGroup|>
|
||||
<checkboxGroup.Field
|
||||
@name="requireMessage"
|
||||
@title={{i18n "admin.config_areas.flags.form.require_message"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Checkbox>
|
||||
{{i18n
|
||||
"admin.config_areas.flags.form.require_message_description"
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</field.Checkbox>
|
||||
</checkboxGroup.Field>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="checkbox-label admin-flag-form__enabled">
|
||||
<input
|
||||
{{on "input" this.onToggleEnabled}}
|
||||
type="checkbox"
|
||||
checked={{this.enabled}}
|
||||
/>
|
||||
{{i18n "admin.config_areas.flags.form.enabled"}}
|
||||
</label>
|
||||
</div>
|
||||
<checkboxGroup.Field
|
||||
@name="enabled"
|
||||
@title={{i18n "admin.config_areas.flags.form.enabled"}}
|
||||
as |field|
|
||||
>
|
||||
<field.Checkbox />
|
||||
</checkboxGroup.Field>
|
||||
</form.CheckboxGroup>
|
||||
|
||||
<div class="alert alert-info admin_flag_form__info">
|
||||
{{dIcon "info-circle"}}
|
||||
{{i18n "admin.config_areas.flags.form.alert"}}
|
||||
</div>
|
||||
<form.Alert @icon="info-circle">
|
||||
{{i18n "admin.config_areas.flags.form.alert"}}
|
||||
</form.Alert>
|
||||
|
||||
<DButton
|
||||
@action={{this.save}}
|
||||
@label="admin.config_areas.flags.form.save"
|
||||
@ariaLabel="admin.config_areas.flags.form.save"
|
||||
@disabled={{not this.isValid}}
|
||||
class="btn-primary admin-flag-form__save"
|
||||
/>
|
||||
<form.Submit @label="admin.config_areas.flags.form.save" />
|
||||
</Form>
|
||||
</AdminConfigAreaCard>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { hash } from "@ember/helper";
|
||||
import FKField from "discourse/form-kit/components/fk/field";
|
||||
import FKFieldset from "discourse/form-kit/components/fk/fieldset";
|
||||
|
||||
const FKCheckboxGroup = <template>
|
||||
<FKFieldset
|
||||
class="form-kit__checkbox-group"
|
||||
@title={{@title}}
|
||||
@description={{@description}}
|
||||
>
|
||||
{{yield
|
||||
(hash
|
||||
Field=(component
|
||||
FKField
|
||||
errors=@errors
|
||||
addError=@addError
|
||||
data=@data
|
||||
set=@set
|
||||
remove=@remove
|
||||
registerField=@registerField
|
||||
unregisterField=@unregisterField
|
||||
triggerRevalidationFor=@triggerRevalidationFor
|
||||
showMeta=false
|
||||
showTitle=false
|
||||
)
|
||||
)
|
||||
}}
|
||||
</FKFieldset>
|
||||
</template>;
|
||||
|
||||
export default FKCheckboxGroup;
|
|
@ -22,7 +22,12 @@ export default class FKControlCheckbox extends Component {
|
|||
...attributes
|
||||
{{on "change" this.handleInput}}
|
||||
/>
|
||||
<span>{{yield}}</span>
|
||||
<span class="form-kit__control-checkbox-content">
|
||||
<span class="form-kit__control-checkbox-title">{{@field.title}}</span>
|
||||
{{#if (has-block)}}
|
||||
<span class="form-kit__control-checkbox-description">{{yield}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
</FKLabel>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import FKText from "discourse/form-kit/components/fk/text";
|
||||
import FKFieldset from "discourse/form-kit/components/fk/fieldset";
|
||||
import FKControlRadioGroupRadio from "./radio-group/radio";
|
||||
|
||||
// eslint-disable-next-line ember/no-empty-glimmer-component-classes
|
||||
|
@ -8,17 +8,12 @@ export default class FKControlRadioGroup extends Component {
|
|||
static controlType = "radio-group";
|
||||
|
||||
<template>
|
||||
<fieldset class="form-kit__radio-group" ...attributes>
|
||||
{{#if @title}}
|
||||
<legend class="form-kit__radio-group-title">{{@title}}</legend>
|
||||
{{/if}}
|
||||
|
||||
{{#if @subtitle}}
|
||||
<FKText class="form-kit__radio-group-subtitle">
|
||||
{{@subtitle}}
|
||||
</FKText>
|
||||
{{/if}}
|
||||
|
||||
<FKFieldset
|
||||
class="form-kit__control-radio-group"
|
||||
@title={{@title}}
|
||||
@subtitle={{@subtitle}}
|
||||
...attributes
|
||||
>
|
||||
{{yield
|
||||
(hash
|
||||
Radio=(component
|
||||
|
@ -26,6 +21,6 @@ export default class FKControlRadioGroup extends Component {
|
|||
)
|
||||
)
|
||||
}}
|
||||
</fieldset>
|
||||
</FKFieldset>
|
||||
</template>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import FKText from "discourse/form-kit/components/fk/text";
|
||||
|
||||
const FKFieldset = <template>
|
||||
<fieldset name={{@name}} class="form-kit__fieldset" ...attributes>
|
||||
{{#if @title}}
|
||||
<legend class="form-kit__fieldset-title">{{@title}}</legend>
|
||||
{{/if}}
|
||||
|
||||
{{#if @description}}
|
||||
<FKText class="form-kit__fieldset-description">
|
||||
{{@description}}
|
||||
</FKText>
|
||||
{{/if}}
|
||||
|
||||
{{yield}}
|
||||
</fieldset>
|
||||
</template>;
|
||||
|
||||
export default FKFieldset;
|
|
@ -6,14 +6,17 @@ import { action } from "@ember/object";
|
|||
import { service } from "@ember/service";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import FKAlert from "discourse/form-kit/components/fk/alert";
|
||||
import FKCheckboxGroup from "discourse/form-kit/components/fk/checkbox-group";
|
||||
import FKCollection from "discourse/form-kit/components/fk/collection";
|
||||
import FKContainer from "discourse/form-kit/components/fk/container";
|
||||
import FKControlConditionalContent from "discourse/form-kit/components/fk/control/conditional-content";
|
||||
import FKControlInputGroup from "discourse/form-kit/components/fk/control/input-group";
|
||||
import FKErrorsSummary from "discourse/form-kit/components/fk/errors-summary";
|
||||
import FKField from "discourse/form-kit/components/fk/field";
|
||||
import FKFieldset from "discourse/form-kit/components/fk/fieldset";
|
||||
import FKInputGroup from "discourse/form-kit/components/fk/input-group";
|
||||
import Row from "discourse/form-kit/components/fk/row";
|
||||
import FKSection from "discourse/form-kit/components/fk/section";
|
||||
import FKSubmit from "discourse/form-kit/components/fk/submit";
|
||||
import { VALIDATION_TYPES } from "discourse/form-kit/lib/constants";
|
||||
import FKFieldData from "discourse/form-kit/lib/fk-field-data";
|
||||
import FKFormData from "discourse/form-kit/lib/fk-form-data";
|
||||
|
@ -237,17 +240,17 @@ class FKForm extends Component {
|
|||
(hash
|
||||
Row=Row
|
||||
Section=FKSection
|
||||
Fieldset=FKFieldset
|
||||
ConditionalContent=(component FKControlConditionalContent)
|
||||
Container=FKContainer
|
||||
Actions=(component FKSection class="form-kit__actions")
|
||||
Button=(component DButton class="form-kit__button")
|
||||
Alert=FKAlert
|
||||
Submit=(component
|
||||
DButton
|
||||
FKSubmit
|
||||
action=this.onSubmit
|
||||
forwardEvent=true
|
||||
class="btn-primary form-kit__button"
|
||||
label="submit"
|
||||
type="submit"
|
||||
isLoading=this.isSubmitting
|
||||
)
|
||||
|
@ -280,7 +283,18 @@ class FKForm extends Component {
|
|||
triggerRevalidationFor=this.triggerRevalidationFor
|
||||
)
|
||||
InputGroup=(component
|
||||
FKControlInputGroup
|
||||
FKInputGroup
|
||||
errors=this.formData.errors
|
||||
addError=this.addError
|
||||
data=this.formData
|
||||
set=this.set
|
||||
remove=this.remove
|
||||
registerField=this.registerField
|
||||
unregisterField=this.unregisterField
|
||||
triggerRevalidationFor=this.triggerRevalidationFor
|
||||
)
|
||||
CheckboxGroup=(component
|
||||
FKCheckboxGroup
|
||||
errors=this.formData.errors
|
||||
addError=this.addError
|
||||
data=this.formData
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { hash } from "@ember/helper";
|
||||
import FKField from "discourse/form-kit/components/fk/field";
|
||||
|
||||
const FKControlInputGroup = <template>
|
||||
const FKInputGroup = <template>
|
||||
<div class="form-kit__input-group">
|
||||
{{yield
|
||||
(hash
|
||||
|
@ -22,4 +22,4 @@ const FKControlInputGroup = <template>
|
|||
</div>
|
||||
</template>;
|
||||
|
||||
export default FKControlInputGroup;
|
||||
export default FKInputGroup;
|
|
@ -0,0 +1,20 @@
|
|||
import Component from "@glimmer/component";
|
||||
import DButton from "discourse/components/d-button";
|
||||
|
||||
export default class FKSubmit extends Component {
|
||||
get label() {
|
||||
return this.args.label ?? "submit";
|
||||
}
|
||||
|
||||
<template>
|
||||
<DButton
|
||||
@label={{this.label}}
|
||||
@action={{@onSubmit}}
|
||||
@forwardEvent="true"
|
||||
class="btn-primary form-kit__button"
|
||||
type="submit"
|
||||
isLoading={{@isSubmitting}}
|
||||
...attributes
|
||||
/>
|
||||
</template>
|
||||
}
|
|
@ -2,6 +2,38 @@ import { capitalize } from "@ember/string";
|
|||
import QUnit from "qunit";
|
||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
class FieldsetHelper {
|
||||
constructor(element, context, name) {
|
||||
this.element = element;
|
||||
this.name = name;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
hasTitle(title, message) {
|
||||
this.context
|
||||
.dom(this.element.querySelector(".form-kit__fieldset-title"))
|
||||
.hasText(title, message);
|
||||
}
|
||||
|
||||
hasDescription(description, message) {
|
||||
this.context
|
||||
.dom(this.element.querySelector(".form-kit__fieldset-description"))
|
||||
.hasText(description, message);
|
||||
}
|
||||
|
||||
includesText(content, message) {
|
||||
this.context.dom(this.element).includesText(content, message);
|
||||
}
|
||||
|
||||
doesNotExist(message) {
|
||||
this.context.dom(this.element).doesNotExist(message);
|
||||
}
|
||||
|
||||
exists(message) {
|
||||
this.context.dom(this.element).exists(message);
|
||||
}
|
||||
}
|
||||
|
||||
class FieldHelper {
|
||||
constructor(element, context, name) {
|
||||
this.element = element;
|
||||
|
@ -74,12 +106,40 @@ class FieldHelper {
|
|||
}
|
||||
}
|
||||
|
||||
get isDisabled() {
|
||||
isEnabled(message) {
|
||||
this.context.notOk(this.disabled, message);
|
||||
}
|
||||
|
||||
hasValue(value, message) {
|
||||
this.context.deepEqual(this.value, value, message);
|
||||
}
|
||||
|
||||
isDisabled(message) {
|
||||
this.context.ok(this.disabled, message);
|
||||
}
|
||||
|
||||
get disabled() {
|
||||
this.context
|
||||
.dom(this.element)
|
||||
.exists(`Could not find field (name: ${this.name}).`);
|
||||
|
||||
return this.element.dataset.disabled === "";
|
||||
this.context.ok(this.element.dataset.disabled === "");
|
||||
}
|
||||
|
||||
hasTitle(title, message) {
|
||||
switch (this.element.dataset.controlType) {
|
||||
case "checkbox": {
|
||||
this.context
|
||||
.dom(this.element.querySelector(".form-kit__control-checkbox-title"))
|
||||
.hasText(title, message);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.context
|
||||
.dom(this.element.querySelector(".form-kit__container-title"))
|
||||
.hasText(title, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasSubtitle(subtitle, message) {
|
||||
|
@ -89,9 +149,23 @@ class FieldHelper {
|
|||
}
|
||||
|
||||
hasDescription(description, message) {
|
||||
this.context
|
||||
.dom(this.element.querySelector(".form-kit__meta-description"))
|
||||
.hasText(description, message);
|
||||
switch (this.element.dataset.controlType) {
|
||||
case "checkbox": {
|
||||
this.context
|
||||
.dom(
|
||||
this.element.querySelector(
|
||||
".form-kit__control-checkbox-description"
|
||||
)
|
||||
)
|
||||
.hasText(description, message);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.context
|
||||
.dom(this.element.querySelector(".form-kit__meta-description"))
|
||||
.hasText(description, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasCharCounter(current, max, message) {
|
||||
|
@ -154,54 +228,18 @@ class FormHelper {
|
|||
name
|
||||
);
|
||||
}
|
||||
|
||||
fieldset(name) {
|
||||
return new FieldsetHelper(
|
||||
query(`.form-kit__fieldset[name="${name}"]`, this.element),
|
||||
this.context,
|
||||
name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function setupFormKitAssertions() {
|
||||
QUnit.assert.form = function (selector = "form") {
|
||||
const form = new FormHelper(selector, this);
|
||||
return {
|
||||
hasErrors: (fields, message) => {
|
||||
form.hasErrors(fields, message);
|
||||
},
|
||||
hasNoErrors: (fields, message) => {
|
||||
form.hasNoErrors(fields, message);
|
||||
},
|
||||
field: (name) => {
|
||||
const field = form.field(name);
|
||||
|
||||
return {
|
||||
doesNotExist: (message) => {
|
||||
field.doesNotExist(message);
|
||||
},
|
||||
hasSubtitle: (value, message) => {
|
||||
field.hasSubtitle(value, message);
|
||||
},
|
||||
hasDescription: (value, message) => {
|
||||
field.hasDescription(value, message);
|
||||
},
|
||||
exists: (message) => {
|
||||
field.exists(message);
|
||||
},
|
||||
isDisabled: (message) => {
|
||||
this.ok(field.disabled, message);
|
||||
},
|
||||
isEnabled: (message) => {
|
||||
this.notOk(field.disabled, message);
|
||||
},
|
||||
hasError: (message) => {
|
||||
field.hasError(message);
|
||||
},
|
||||
hasCharCounter: (current, max, message) => {
|
||||
field.hasCharCounter(current, max, message);
|
||||
},
|
||||
hasNoError: (message) => {
|
||||
field.hasNoError(message);
|
||||
},
|
||||
hasValue: (value, message) => {
|
||||
this.deepEqual(field.value, value, message);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
return new FormHelper(selector, this);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import Form from "discourse/components/form";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
|
||||
module(
|
||||
"Integration | Component | FormKit | Layout | CheckboxGroup",
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("default", async function (assert) {
|
||||
await render(<template>
|
||||
<Form as |form|>
|
||||
<form.CheckboxGroup as |checkboxGroup|>
|
||||
<checkboxGroup.Field @name="foo" @title="Foo" as |field|>
|
||||
<field.Checkbox />
|
||||
</checkboxGroup.Field>
|
||||
<checkboxGroup.Field @name="bar" @title="Bar" as |field|>
|
||||
<field.Checkbox>A description</field.Checkbox>
|
||||
</checkboxGroup.Field>
|
||||
</form.CheckboxGroup>
|
||||
</Form>
|
||||
</template>);
|
||||
|
||||
assert.form().field("foo").hasTitle("Foo");
|
||||
assert.form().field("bar").hasTitle("Bar");
|
||||
assert.form().field("bar").hasDescription("A description");
|
||||
});
|
||||
}
|
||||
);
|
|
@ -0,0 +1,38 @@
|
|||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import Form from "discourse/components/form";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
|
||||
module(
|
||||
"Integration | Component | FormKit | Layout | Fieldset",
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("default", async function (assert) {
|
||||
await render(<template>
|
||||
<Form as |form|>
|
||||
<form.Fieldset
|
||||
@title="Title"
|
||||
@description="Description"
|
||||
@name="a-fieldset"
|
||||
>
|
||||
Yielded content
|
||||
</form.Fieldset>
|
||||
</Form>
|
||||
</template>);
|
||||
|
||||
assert
|
||||
.form()
|
||||
.fieldset("a-fieldset")
|
||||
.hasTitle("Title", "it renders a title");
|
||||
assert
|
||||
.form()
|
||||
.fieldset("a-fieldset")
|
||||
.hasDescription("Description", "it renders a description");
|
||||
assert
|
||||
.form()
|
||||
.fieldset("a-fieldset")
|
||||
.includesText("Yielded content", "it yields its content");
|
||||
});
|
||||
}
|
||||
);
|
|
@ -26,4 +26,16 @@ module("Integration | Component | FormKit | Layout | Submit", function (hooks) {
|
|||
assert.dom(".form-kit__button.btn-primary").hasText(I18n.t("submit"));
|
||||
assert.deepEqual(value, 1);
|
||||
});
|
||||
|
||||
test("@label", async function (assert) {
|
||||
await render(<template>
|
||||
<Form as |form|>
|
||||
<form.Submit @label="cancel" />
|
||||
</Form>
|
||||
</template>);
|
||||
|
||||
assert
|
||||
.dom(".form-kit__button")
|
||||
.hasText(I18n.t("cancel"), "it allows to override the label");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -36,37 +36,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.admin-flag-form {
|
||||
&__enabled,
|
||||
&__applies-to {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
&__save {
|
||||
margin-top: 1em;
|
||||
}
|
||||
&__info {
|
||||
color: var(--primary-high);
|
||||
svg {
|
||||
color: var(--tertiary);
|
||||
}
|
||||
}
|
||||
&__description {
|
||||
width: 60%;
|
||||
}
|
||||
&__applies-to.select-kit.multi-select {
|
||||
width: 60%;
|
||||
}
|
||||
&__require-message-description {
|
||||
clear: both;
|
||||
flex-basis: 100%;
|
||||
font-size: var(--font-down-2);
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
label {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-flags__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
width: 100%;
|
||||
border-radius: var(--d-border-radius);
|
||||
box-sizing: border-box;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
.form-kit__checkbox-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75em;
|
||||
}
|
|
@ -1,25 +1,31 @@
|
|||
.form-kit__control-checkbox {
|
||||
&[type="checkbox"] {
|
||||
margin: 0.17em;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
.form-kit {
|
||||
&__control-checkbox {
|
||||
&[type="checkbox"] {
|
||||
margin: 0.17em;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&-label {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
font-weight: normal !important;
|
||||
margin: 0;
|
||||
color: var(--primary);
|
||||
&-label {
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
font-weight: normal !important;
|
||||
margin: 0;
|
||||
color: var(--primary);
|
||||
|
||||
.form-kit__field[data-disabled] & {
|
||||
cursor: not-allowed;
|
||||
.form-kit__field[data-disabled] & {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.form-kit__field-checkbox {
|
||||
+ .form-kit__field-checkbox {
|
||||
margin-top: calc(-1 * var(--form-kit-gutter-y));
|
||||
&__control-checkbox-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
&__control-checkbox-description {
|
||||
color: var(--primary-medium);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
.form-kit__radio-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75em;
|
||||
|
||||
&-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25em;
|
||||
margin: 0;
|
||||
font-size: var(--font-down-1-rem);
|
||||
color: var(--primary-high);
|
||||
}
|
||||
}
|
|
@ -16,11 +16,11 @@
|
|||
.form-kit__control-radio-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.form-kit__control-radio-description {
|
||||
color: var(--primary-medium);
|
||||
font-size: var(--font-down-1-rem);
|
||||
}
|
||||
|
||||
.form-kit__inline-radio {
|
||||
|
|
|
@ -10,6 +10,6 @@
|
|||
// prevents firefox/chrome to add spacing under textarea
|
||||
display: block;
|
||||
|
||||
height: 150px !important;
|
||||
height: 150px;
|
||||
border-radius: var(--d-input-border-radius);
|
||||
}
|
||||
|
|
22
app/assets/stylesheets/common/form-kit/_fieldset.scss
Normal file
22
app/assets/stylesheets/common/form-kit/_fieldset.scss
Normal file
|
@ -0,0 +1,22 @@
|
|||
.form-kit {
|
||||
&__fieldset {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
&__fieldset-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0 0 0.25rem;
|
||||
font-size: var(--font-down-1-rem);
|
||||
color: var(--primary-high);
|
||||
}
|
||||
|
||||
&__fieldset-description {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@import "_default-input-mixin";
|
||||
@import "_fieldset";
|
||||
@import "_alert";
|
||||
@import "_char-counter";
|
||||
@import "_col";
|
||||
|
@ -13,9 +14,9 @@
|
|||
@import "_control-image";
|
||||
@import "_control-input";
|
||||
@import "_control-input-group";
|
||||
@import "_checkbox-group";
|
||||
@import "_control-menu";
|
||||
@import "_control-radio";
|
||||
@import "_control-radio-group";
|
||||
@import "_control-select";
|
||||
@import "_control-custom";
|
||||
@import "_control-textarea";
|
||||
|
|
|
@ -5,13 +5,3 @@
|
|||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-config-area__primary-content {
|
||||
.admin-flag-form {
|
||||
&__name,
|
||||
&__applies-to.select-kit.multi-select,
|
||||
&__description {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5560,6 +5560,7 @@ en:
|
|||
name: "Name"
|
||||
description: "Description"
|
||||
applies_to: "Display this flag on"
|
||||
invalid_applies_to: "Required"
|
||||
topic: "topics"
|
||||
post: "posts"
|
||||
chat_message: "chat messages"
|
||||
|
|
|
@ -79,11 +79,25 @@
|
|||
</Form>
|
||||
</StyleguideExample>
|
||||
|
||||
<StyleguideExample @title="Checkbox">
|
||||
<StyleguideExample @title="CheckboxGroup">
|
||||
<Form as |form|>
|
||||
<form.Field @title="Contract" @name="contract" as |field|>
|
||||
<field.Checkbox>Accept the contract</field.Checkbox>
|
||||
</form.Field>
|
||||
<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>
|
||||
|
||||
|
|
|
@ -2,163 +2,162 @@
|
|||
|
||||
describe "Admin Flags Page", type: :system do
|
||||
fab!(:admin)
|
||||
fab!(:topic)
|
||||
fab!(:post) { Fabricate(:post, topic: topic) }
|
||||
fab!(:post)
|
||||
|
||||
let(:topic_page) { PageObjects::Pages::Topic.new }
|
||||
let(:admin_flags_page) { PageObjects::Pages::AdminFlags.new }
|
||||
let(:admin_flag_form_page) { PageObjects::Pages::AdminFlagForm.new }
|
||||
let(:flag_modal) { PageObjects::Modals::Flag.new }
|
||||
|
||||
before { sign_in(admin) }
|
||||
|
||||
it "allows admin to disable, change order, create, update and delete flags" do
|
||||
# disable
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Spam", "It's Illegal", "Something Else"],
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
expect(flag_modal).to have_choices(
|
||||
"It's Inappropriate",
|
||||
"It's Spam",
|
||||
"It's Illegal",
|
||||
"Something Else",
|
||||
)
|
||||
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.toggle("spam")
|
||||
expect(page).not_to have_css(".admin-flag-item.spam.saving")
|
||||
admin_flags_page.visit.toggle("spam")
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Illegal", "Something Else"],
|
||||
)
|
||||
expect(flag_modal).to have_choices("It's Inappropriate", "It's Illegal", "Something Else")
|
||||
|
||||
Flag.system.where(name: "spam").update!(enabled: true)
|
||||
|
||||
# change order
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Spam", "It's Illegal", "Something Else"],
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
expect(flag_modal).to have_choices(
|
||||
"It's Inappropriate",
|
||||
"It's Spam",
|
||||
"It's Illegal",
|
||||
"Something Else",
|
||||
)
|
||||
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.move_down("spam")
|
||||
expect(page).not_to have_css(".admin-flag-item.spam.saving")
|
||||
admin_flags_page.visit.move_down("spam")
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Illegal", "It's Spam", "Something Else"],
|
||||
expect(flag_modal).to have_choices(
|
||||
"It's Inappropriate",
|
||||
"It's Illegal",
|
||||
"It's Spam",
|
||||
"Something Else",
|
||||
)
|
||||
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.move_up("spam")
|
||||
expect(page).not_to have_css(".admin-flag-item.spam.saving")
|
||||
admin_flags_page.visit.move_up("spam")
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Spam", "It's Illegal", "Something Else"],
|
||||
expect(flag_modal).to have_choices(
|
||||
"It's Inappropriate",
|
||||
"It's Spam",
|
||||
"It's Illegal",
|
||||
"Something Else",
|
||||
)
|
||||
|
||||
# create
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Spam", "It's Illegal", "Something Else"],
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
expect(flag_modal).to have_choices(
|
||||
"It's Inappropriate",
|
||||
"It's Spam",
|
||||
"It's Illegal",
|
||||
"Something Else",
|
||||
)
|
||||
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.visit.click_add_flag
|
||||
admin_flag_form_page
|
||||
.fill_in_name("Vulgar")
|
||||
.fill_in_description("New flag description")
|
||||
.select_applies_to("Topic")
|
||||
.select_applies_to("Post")
|
||||
.click_save
|
||||
|
||||
admin_flags_page.click_add_flag
|
||||
|
||||
expect(admin_flag_form_page).to have_disabled_save_button
|
||||
|
||||
admin_flag_form_page.fill_in_name("Vulgar")
|
||||
admin_flag_form_page.fill_in_description("New flag description")
|
||||
admin_flag_form_page.fill_in_applies_to("Topic")
|
||||
admin_flag_form_page.fill_in_applies_to("Post")
|
||||
admin_flag_form_page.click_save
|
||||
|
||||
expect(all(".admin-flag-item__name").map(&:text)).to eq(
|
||||
[
|
||||
"Send @%{username} a message",
|
||||
"Off-Topic",
|
||||
"Inappropriate",
|
||||
"Spam",
|
||||
"Illegal",
|
||||
"Something Else",
|
||||
"Vulgar",
|
||||
],
|
||||
expect(admin_flags_page).to have_flags(
|
||||
"Send @%{username} a message",
|
||||
"Off-Topic",
|
||||
"Inappropriate",
|
||||
"Spam",
|
||||
"Illegal",
|
||||
"Something Else",
|
||||
"Vulgar",
|
||||
)
|
||||
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Spam", "It's Illegal", "Something Else", "Vulgar"],
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
expect(flag_modal).to have_choices(
|
||||
"It's Inappropriate",
|
||||
"It's Spam",
|
||||
"It's Illegal",
|
||||
"Something Else",
|
||||
"Vulgar",
|
||||
)
|
||||
|
||||
# update
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.visit.click_edit_flag("vulgar")
|
||||
admin_flag_form_page.fill_in_name("Tasteless").click_save
|
||||
|
||||
admin_flags_page.click_edit_flag("vulgar")
|
||||
|
||||
admin_flag_form_page.fill_in_name("Tasteless")
|
||||
admin_flag_form_page.click_save
|
||||
|
||||
expect(all(".admin-flag-item__name").map(&:text)).to eq(
|
||||
[
|
||||
"Send @%{username} a message",
|
||||
"Off-Topic",
|
||||
"Inappropriate",
|
||||
"Spam",
|
||||
"Illegal",
|
||||
"Something Else",
|
||||
"Tasteless",
|
||||
],
|
||||
expect(admin_flags_page).to have_flags(
|
||||
"Send @%{username} a message",
|
||||
"Off-Topic",
|
||||
"Inappropriate",
|
||||
"Spam",
|
||||
"Illegal",
|
||||
"Something Else",
|
||||
"Tasteless",
|
||||
)
|
||||
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Spam", "It's Illegal", "Something Else", "Tasteless"],
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
expect(flag_modal).to have_choices(
|
||||
"It's Inappropriate",
|
||||
"It's Spam",
|
||||
"It's Illegal",
|
||||
"Something Else",
|
||||
"Tasteless",
|
||||
)
|
||||
|
||||
# delete
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.click_delete_flag("tasteless")
|
||||
admin_flags_page.confirm_delete
|
||||
expect(page).not_to have_css(".admin-flag-item.tasteless.saving")
|
||||
admin_flags_page.visit.click_delete_flag("tasteless").confirm_delete
|
||||
|
||||
topic_page.visit_topic(post.topic)
|
||||
topic_page.open_flag_topic_modal
|
||||
expect(all(".flag-action-type-details strong").map(&:text)).to eq(
|
||||
["It's Inappropriate", "It's Spam", "It's Illegal", "Something Else"],
|
||||
expect(admin_flags_page).to have_no_flag("tasteless")
|
||||
|
||||
topic_page.visit_topic(post.topic).open_flag_topic_modal
|
||||
|
||||
expect(flag_modal).to have_choices(
|
||||
"It's Inappropriate",
|
||||
"It's Spam",
|
||||
"It's Illegal",
|
||||
"Something Else",
|
||||
)
|
||||
end
|
||||
|
||||
it "does not allow to move notify user flag" do
|
||||
visit "/admin/config/flags"
|
||||
expect(page).not_to have_css(".notify_user .flag-menu-trigger")
|
||||
admin_flags_page.visit
|
||||
expect(admin_flags_page).to have_no_action_for_flag("notify_user")
|
||||
end
|
||||
|
||||
it "does not allow bottom flag to move down" do
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.open_flag_menu("notify_moderators")
|
||||
expect(page).not_to have_css(".dropdown-menu__item .move-down")
|
||||
admin_flags_page.visit.open_flag_menu("notify_moderators")
|
||||
expect(admin_flags_page).to have_no_item_action("move-down")
|
||||
end
|
||||
|
||||
it "does not allow to system flag to be edited" do
|
||||
visit "/admin/config/flags"
|
||||
expect(page).to have_css(".off_topic .admin-flag-item__edit[disabled]")
|
||||
admin_flags_page.visit
|
||||
expect(admin_flags_page).to have_disabled_edit_for_flag("off_topic")
|
||||
end
|
||||
|
||||
it "does not allow to system flag to be deleted" do
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.open_flag_menu("notify_moderators")
|
||||
expect(page).to have_css(".admin-flag-item__delete[disabled]")
|
||||
admin_flags_page.visit.open_flag_menu("notify_moderators")
|
||||
expect(admin_flags_page).to have_disabled_item_action("delete")
|
||||
end
|
||||
|
||||
it "does not allow top flag to move up" do
|
||||
visit "/admin/config/flags"
|
||||
admin_flags_page.open_flag_menu("off_topic")
|
||||
expect(page).not_to have_css(".dropdown-menu__item .move-up")
|
||||
admin_flags_page.visit.open_flag_menu("off_topic")
|
||||
expect(admin_flags_page).to have_no_item_action("move-up")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,6 +28,10 @@ module PageObjects
|
|||
def check_confirmation
|
||||
body.check("confirmation")
|
||||
end
|
||||
|
||||
def has_choices?(*choices)
|
||||
body.all(".flag-action-type-details strong").map(&:text) == choices
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,27 +3,30 @@
|
|||
module PageObjects
|
||||
module Pages
|
||||
class AdminFlagForm < PageObjects::Pages::Base
|
||||
def has_disabled_save_button?
|
||||
find_button("Save", disabled: true)
|
||||
end
|
||||
|
||||
def fill_in_name(name)
|
||||
find(".admin-flag-form__name").fill_in(with: name)
|
||||
form.field("name").fill_in(name)
|
||||
self
|
||||
end
|
||||
|
||||
def fill_in_description(description)
|
||||
find(".admin-flag-form__description").fill_in(with: description)
|
||||
form.field("description").fill_in(description)
|
||||
self
|
||||
end
|
||||
|
||||
def fill_in_applies_to(applies_to)
|
||||
def select_applies_to(applies_to)
|
||||
dropdown = PageObjects::Components::SelectKit.new(".admin-flag-form__applies-to")
|
||||
dropdown.expand
|
||||
dropdown.select_row_by_value(applies_to)
|
||||
dropdown.collapse
|
||||
self
|
||||
end
|
||||
|
||||
def click_save
|
||||
find(".admin-flag-form__save").click
|
||||
form.submit
|
||||
end
|
||||
|
||||
def form
|
||||
@form ||= PageObjects::Components::FormKit.new(".admin-flag-form .form-kit")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,39 +3,96 @@
|
|||
module PageObjects
|
||||
module Pages
|
||||
class AdminFlags < PageObjects::Pages::Base
|
||||
def visit
|
||||
page.visit("/admin/config/flags")
|
||||
self
|
||||
end
|
||||
|
||||
def toggle(key)
|
||||
PageObjects::Components::DToggleSwitch.new(".admin-flag-item__toggle.#{key}").toggle
|
||||
has_saved_flag?(key)
|
||||
self
|
||||
end
|
||||
|
||||
def open_flag_menu(key)
|
||||
find(".#{key} .flag-menu-trigger").click
|
||||
self
|
||||
end
|
||||
|
||||
def has_action_for_flag?(flag)
|
||||
has_selector?(".#{flag} .flag-menu-trigger")
|
||||
end
|
||||
|
||||
def has_no_action_for_flag?(flag)
|
||||
has_no_selector?(".#{flag} .flag-menu-trigger")
|
||||
end
|
||||
|
||||
def has_disabled_edit_for_flag?(flag)
|
||||
has_selector?(".#{flag} .admin-flag-item__edit[disabled]")
|
||||
end
|
||||
|
||||
def has_disabled_item_action?(action)
|
||||
has_selector?(".admin-flag-item__#{action}[disabled]")
|
||||
end
|
||||
|
||||
def has_item_action?(action)
|
||||
has_selector?(".admin-flag-item__#{action}")
|
||||
end
|
||||
|
||||
def has_no_item_action?(action)
|
||||
has_no_selector?(".admin-flag-item__#{action}")
|
||||
end
|
||||
|
||||
def has_flags?(*flags)
|
||||
all(".admin-flag-item__name").map(&:text) == flags
|
||||
end
|
||||
|
||||
def has_flag?(flag)
|
||||
has_css?(".admin-flag-item.#{flag}")
|
||||
end
|
||||
|
||||
def has_no_flag?(flag)
|
||||
has_no_css?(".admin-flag-item.#{flag}")
|
||||
end
|
||||
|
||||
def has_saved_flag?(key)
|
||||
has_css?(".admin-flag-item.#{key}.saving")
|
||||
has_no_css?(".admin-flag-item.#{key}.saving")
|
||||
end
|
||||
|
||||
def move_down(key)
|
||||
open_flag_menu(key)
|
||||
find(".admin-flag-item__move-down").click
|
||||
has_saved_flag?(key)
|
||||
self
|
||||
end
|
||||
|
||||
def move_up(key)
|
||||
open_flag_menu(key)
|
||||
find(".admin-flag-item__move-up").click
|
||||
has_saved_flag?(key)
|
||||
self
|
||||
end
|
||||
|
||||
def click_add_flag
|
||||
find(".admin-flags__header-add-flag").click
|
||||
self
|
||||
end
|
||||
|
||||
def click_edit_flag(key)
|
||||
find(".#{key} .admin-flag-item__edit").click
|
||||
self
|
||||
end
|
||||
|
||||
def click_delete_flag(key)
|
||||
find(".#{key} .flag-menu-trigger").click
|
||||
find(".admin-flag-item__delete").click
|
||||
self
|
||||
end
|
||||
|
||||
def confirm_delete
|
||||
find(".dialog-footer .btn-primary").click
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user