FEATURE: modal for admins to edit Community section (#21668)
Allow admins to edit Community section. This includes drag and drop reorder, change names, delete and reset to default. Visual improvements introduced in edit community section modal are available in edit custom section form as well. For example: - drag and drop links to change their position; - smaller icon picker.
This commit is contained in:
parent
7d9a823a55
commit
9f78ff5572
|
@ -7,51 +7,50 @@
|
|||
@class={{this.section.dragCss}}
|
||||
>
|
||||
{{#each this.section.links as |link|}}
|
||||
{{#if link.shouldDisplay}}
|
||||
{{#if link.external}}
|
||||
<Sidebar::SectionLink
|
||||
@linkName={{link.name}}
|
||||
@content={{replace-emoji link.text}}
|
||||
@prefixType="icon"
|
||||
@prefixValue={{link.prefixValue}}
|
||||
@href={{link.value}}
|
||||
@class={{link.linkDragCss}}
|
||||
{{draggable
|
||||
{{#if link.external}}
|
||||
<Sidebar::SectionLink
|
||||
@shouldDisplay={{link.shouldDisplay}}
|
||||
@linkName={{link.name}}
|
||||
@content={{replace-emoji link.text}}
|
||||
@prefixType="icon"
|
||||
@prefixValue={{link.prefixValue}}
|
||||
@href={{link.value}}
|
||||
@class={{link.linkDragCss}}
|
||||
{{draggable
|
||||
didStartDrag=link.didStartDrag
|
||||
didEndDrag=link.didEndDrag
|
||||
dragMove=link.dragMove
|
||||
}}
|
||||
/>
|
||||
{{else}}
|
||||
<Sidebar::SectionLink
|
||||
@shouldDisplay={{link.shouldDisplay}}
|
||||
@href={{link.href}}
|
||||
@title={{link.title}}
|
||||
@linkName={{link.name}}
|
||||
@route={{link.route}}
|
||||
@model={{link.model}}
|
||||
@models={{link.models}}
|
||||
@query={{link.query}}
|
||||
@content={{replace-emoji link.text}}
|
||||
@badgeText={{link.badgeText}}
|
||||
@prefixType="icon"
|
||||
@prefixValue={{link.prefixValue}}
|
||||
@suffixCSSClass={{link.suffixCSSClass}}
|
||||
@suffixValue={{link.suffixValue}}
|
||||
@suffixType={{link.suffixType}}
|
||||
@currentWhen={{link.currentWhen}}
|
||||
@class={{link.linkDragCss}}
|
||||
{{(if
|
||||
link.didStartDrag
|
||||
(modifier
|
||||
"draggable"
|
||||
didStartDrag=link.didStartDrag
|
||||
didEndDrag=link.didEndDrag
|
||||
dragMove=link.dragMove
|
||||
}}
|
||||
/>
|
||||
{{else}}
|
||||
<Sidebar::SectionLink
|
||||
@shouldDisplay={{link.shouldDisplay}}
|
||||
@href={{link.href}}
|
||||
@title={{link.title}}
|
||||
@linkName={{link.name}}
|
||||
@route={{link.route}}
|
||||
@model={{link.model}}
|
||||
@models={{link.models}}
|
||||
@query={{link.query}}
|
||||
@content={{replace-emoji link.text}}
|
||||
@badgeText={{link.badgeText}}
|
||||
@prefixType="icon"
|
||||
@prefixValue={{link.prefixValue}}
|
||||
@suffixCSSClass={{link.suffixCSSClass}}
|
||||
@suffixValue={{link.suffixValue}}
|
||||
@suffixType={{link.suffixType}}
|
||||
@currentWhen={{link.currentWhen}}
|
||||
@class={{link.linkDragCss}}
|
||||
{{(if
|
||||
link.didStartDrag
|
||||
(modifier
|
||||
"draggable"
|
||||
didStartDrag=link.didStartDrag
|
||||
didEndDrag=link.didEndDrag
|
||||
dragMove=link.dragMove
|
||||
)
|
||||
)}}
|
||||
/>
|
||||
{{/if}}
|
||||
)
|
||||
)}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
<Sidebar::SectionLink
|
||||
@shouldDisplay={{@sectionLink.shouldDisplay}}
|
||||
@linkName={{@sectionLink.name}}
|
||||
@route={{@sectionLink.route}}
|
||||
@href={{@sectionLink.href}}
|
||||
@query={{@sectionLink.query}}
|
||||
@title={{@sectionLink.title}}
|
||||
@content={{@sectionLink.text}}
|
||||
@currentWhen={{@sectionLink.currentWhen}}
|
||||
@badgeText={{@sectionLink.badgeText}}
|
||||
@model={{@sectionLink.model}}
|
||||
@models={{@sectionLink.models}}
|
||||
@prefixType={{@sectionLink.prefixType}}
|
||||
@prefixValue={{@sectionLink.prefixValue}}
|
||||
/>
|
||||
{{#if @sectionLink.external}}
|
||||
<Sidebar::SectionLink
|
||||
@shouldDisplay={{@sectionLink.shouldDisplay}}
|
||||
@linkName={{@sectionLink.name}}
|
||||
@content={{replace-emoji @sectionLink.text}}
|
||||
@prefixType="icon"
|
||||
@prefixValue={{@sectionLink.prefixValue}}
|
||||
@href={{@sectionLink.value}}
|
||||
/>
|
||||
{{else}}
|
||||
<Sidebar::SectionLink
|
||||
@shouldDisplay={{@sectionLink.shouldDisplay}}
|
||||
@href={{@sectionLink.href}}
|
||||
@title={{@sectionLink.title}}
|
||||
@linkName={{@sectionLink.name}}
|
||||
@route={{@sectionLink.route}}
|
||||
@model={{@sectionLink.model}}
|
||||
@models={{@sectionLink.models}}
|
||||
@query={{@sectionLink.query}}
|
||||
@content={{replace-emoji @sectionLink.text}}
|
||||
@badgeText={{@sectionLink.badgeText}}
|
||||
@prefixType="icon"
|
||||
@prefixValue={{@sectionLink.prefixValue}}
|
||||
@suffixCSSClass={{@sectionLink.suffixCSSClass}}
|
||||
@suffixValue={{@sectionLink.suffixValue}}
|
||||
@suffixType={{@sectionLink.suffixType}}
|
||||
@currentWhen={{@sectionLink.currentWhen}}
|
||||
/>
|
||||
{{/if}}
|
|
@ -0,0 +1,72 @@
|
|||
<div
|
||||
class={{concat-class
|
||||
"sidebar-section-form-link"
|
||||
"row-wrapper"
|
||||
this.dragCssClass
|
||||
}}
|
||||
draggable="true"
|
||||
{{on "dragstart" this.dragHasStarted}}
|
||||
{{on "dragover" this.dragOver}}
|
||||
{{on "dragenter" this.dragEnter}}
|
||||
{{on "dragleave" this.dragLeave}}
|
||||
{{on "dragend" this.dragEnd}}
|
||||
{{on "drop" this.dropItem}}
|
||||
>
|
||||
<div class="draggable" data-link-name={{@link.name}}>
|
||||
{{d-icon "grip-lines"}}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<IconPicker
|
||||
@name="icon"
|
||||
@value={{@link.icon}}
|
||||
@options={{hash
|
||||
maximum=1
|
||||
caretDownIcon="caret-down"
|
||||
caretUpIcon="caret-up"
|
||||
icons=@link.icon
|
||||
}}
|
||||
class={{@link.iconCssClass}}
|
||||
@onlyAvailable={{true}}
|
||||
@onChange={{action (mut @link.icon)}}
|
||||
/>
|
||||
{{#if @link.invalidIconMessage}}
|
||||
<div class="icon warning">
|
||||
{{@link.invalidIconMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<Input
|
||||
name="link-name"
|
||||
@type="text"
|
||||
@value={{@link.name}}
|
||||
class={{@link.nameCssClass}}
|
||||
{{on "input" (action (mut @link.name) value="target.value")}}
|
||||
/>
|
||||
{{#if @link.invalidNameMessage}}
|
||||
<div class="name warning">
|
||||
{{@link.invalidNameMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<Input
|
||||
name="link-url"
|
||||
@type="text"
|
||||
@value={{@link.value}}
|
||||
class={{@link.valueCssClass}}
|
||||
{{on "input" (action (mut @link.value) value="target.value")}}
|
||||
/>
|
||||
{{#if @link.invalidValueMessage}}
|
||||
<div class="value warning">
|
||||
{{@link.invalidValueMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<DButton
|
||||
@icon="trash-alt"
|
||||
@action={{action @deleteLink @link}}
|
||||
@class="btn-flat delete-link"
|
||||
@title="sidebar.sections.custom.links.delete"
|
||||
/>
|
||||
</div>
|
|
@ -0,0 +1,68 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { action } from "@ember/object";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
|
||||
export default class SectionFormLink extends Component {
|
||||
@tracked dragCssClass;
|
||||
|
||||
dragCount = 0;
|
||||
|
||||
isAboveElement(event) {
|
||||
event.preventDefault();
|
||||
const target = event.currentTarget;
|
||||
const domRect = target.getBoundingClientRect();
|
||||
return event.offsetY < domRect.height / 2;
|
||||
}
|
||||
|
||||
@action
|
||||
dragHasStarted(event) {
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
event.dataTransfer.setData("linkId", this.args.link.objectId);
|
||||
this.dragCssClass = "dragging";
|
||||
}
|
||||
|
||||
@action
|
||||
dragOver(event) {
|
||||
event.preventDefault();
|
||||
if (!this.dragCssClass) {
|
||||
if (this.isAboveElement(event)) {
|
||||
this.dragCssClass = "drag-above";
|
||||
} else {
|
||||
this.dragCssClass = "drag-below";
|
||||
}
|
||||
}
|
||||
}
|
||||
@action
|
||||
dragEnter() {
|
||||
this.dragCount++;
|
||||
}
|
||||
|
||||
@action
|
||||
dragLeave() {
|
||||
this.dragCount--;
|
||||
if (
|
||||
this.dragCount === 0 &&
|
||||
(this.dragCssClass === "drag-above" || this.dragCssClass === "drag-below")
|
||||
) {
|
||||
this.dragCssClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
dropItem(event) {
|
||||
event.stopPropagation();
|
||||
this.dragCounter = 0;
|
||||
this.args.reorderCallback(
|
||||
parseInt(event.dataTransfer.getData("linkId"), 10),
|
||||
this.args.link,
|
||||
this.isAboveElement(event)
|
||||
);
|
||||
this.dragCssClass = null;
|
||||
}
|
||||
|
||||
@action
|
||||
dragEnd() {
|
||||
this.dragCounter = 0;
|
||||
this.dragCssClass = null;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import { sanitize } from "discourse/lib/text";
|
|||
import { tracked } from "@glimmer/tracking";
|
||||
import { A } from "@ember/array";
|
||||
import { SIDEBAR_SECTION, SIDEBAR_URL } from "discourse/lib/constants";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
|
||||
const FULL_RELOAD_LINKS_REGEX = [
|
||||
/^\/my\/[a-z_\-\/]+$/,
|
||||
|
@ -19,17 +20,30 @@ const FULL_RELOAD_LINKS_REGEX = [
|
|||
class Section {
|
||||
@tracked title;
|
||||
@tracked links;
|
||||
@tracked secondaryLinks;
|
||||
|
||||
constructor({ title, links, id, publicSection }) {
|
||||
constructor({
|
||||
title,
|
||||
links,
|
||||
secondaryLinks,
|
||||
id,
|
||||
publicSection,
|
||||
sectionType,
|
||||
}) {
|
||||
this.title = title;
|
||||
this.public = publicSection;
|
||||
this.sectionType = sectionType;
|
||||
this.links = links;
|
||||
this.secondaryLinks = secondaryLinks;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
get valid() {
|
||||
const allLinks = this.links
|
||||
.filter((link) => !link._destroy)
|
||||
.concat(this.secondaryLinks?.filter((link) => !link._destroy) || []);
|
||||
const validLinks =
|
||||
this.links.length > 0 && this.links.every((link) => link.valid);
|
||||
allLinks.length > 0 && allLinks.every((link) => link.valid);
|
||||
return this.validTitle && validLinks;
|
||||
}
|
||||
|
||||
|
@ -70,7 +84,7 @@ class SectionLink {
|
|||
@tracked value;
|
||||
@tracked _destroy;
|
||||
|
||||
constructor({ router, icon, name, value, id }) {
|
||||
constructor({ router, icon, name, value, id, objectId, segment }) {
|
||||
this.router = router;
|
||||
this.icon = icon || "link";
|
||||
this.name = name;
|
||||
|
@ -78,6 +92,8 @@ class SectionLink {
|
|||
this.id = id;
|
||||
this.httpHost = "http://" + window.location.host;
|
||||
this.httpsHost = "https://" + window.location.host;
|
||||
this.objectId = objectId;
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
get path() {
|
||||
|
@ -165,6 +181,10 @@ class SectionLink {
|
|||
);
|
||||
}
|
||||
|
||||
get isPrimary() {
|
||||
return this.segment === "primary";
|
||||
}
|
||||
|
||||
get #blankIcon() {
|
||||
return isEmpty(this.icon);
|
||||
}
|
||||
|
@ -221,6 +241,7 @@ export default Controller.extend(ModalFunctionality, {
|
|||
flashText: null,
|
||||
flashClass: null,
|
||||
});
|
||||
this.nextObjectId = 0;
|
||||
this.model = this.initModel();
|
||||
},
|
||||
|
||||
|
@ -233,27 +254,48 @@ export default Controller.extend(ModalFunctionality, {
|
|||
return new Section({
|
||||
title: this.model.title,
|
||||
publicSection: this.model.public,
|
||||
links: A(
|
||||
this.model.links.map(
|
||||
(link) =>
|
||||
new SectionLink({
|
||||
router: this.router,
|
||||
icon: link.icon,
|
||||
name: link.name,
|
||||
value: link.value,
|
||||
id: link.id,
|
||||
})
|
||||
)
|
||||
),
|
||||
sectionType: this.model.section_type,
|
||||
links: this.model.links.reduce((acc, link) => {
|
||||
if (link.segment === "primary") {
|
||||
this.nextObjectId++;
|
||||
acc.push(this.initLink(link));
|
||||
}
|
||||
return acc;
|
||||
}, A()),
|
||||
secondaryLinks: this.model.links.reduce((acc, link) => {
|
||||
if (link.segment === "secondary") {
|
||||
this.nextObjectId++;
|
||||
acc.push(this.initLink(link));
|
||||
}
|
||||
return acc;
|
||||
}, A()),
|
||||
id: this.model.id,
|
||||
});
|
||||
} else {
|
||||
return new Section({
|
||||
links: A([new SectionLink({ router: this.router })]),
|
||||
links: A([
|
||||
new SectionLink({
|
||||
router: this.router,
|
||||
objectId: this.nextObjectId,
|
||||
segment: "primary",
|
||||
}),
|
||||
]),
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
initLink(link) {
|
||||
return new SectionLink({
|
||||
router: this.router,
|
||||
icon: link.icon,
|
||||
name: link.name,
|
||||
value: link.value,
|
||||
id: link.id,
|
||||
objectId: this.nextObjectId,
|
||||
segment: link.segment,
|
||||
});
|
||||
},
|
||||
|
||||
create() {
|
||||
return ajax(`/sidebar_sections`, {
|
||||
type: "POST",
|
||||
|
@ -294,15 +336,18 @@ export default Controller.extend(ModalFunctionality, {
|
|||
data: JSON.stringify({
|
||||
title: this.model.title,
|
||||
public: this.model.public,
|
||||
links: this.model.links.map((link) => {
|
||||
return {
|
||||
id: link.id,
|
||||
icon: link.icon,
|
||||
name: link.name,
|
||||
value: link.path,
|
||||
_destroy: link._destroy,
|
||||
};
|
||||
}),
|
||||
links: this.model.links
|
||||
.concat(this.model?.secondaryLinks || [])
|
||||
.map((link) => {
|
||||
return {
|
||||
id: link.id,
|
||||
icon: link.icon,
|
||||
name: link.name,
|
||||
value: link.path,
|
||||
segment: link.segment,
|
||||
_destroy: link._destroy,
|
||||
};
|
||||
}),
|
||||
}),
|
||||
})
|
||||
.then((data) => {
|
||||
|
@ -329,23 +374,112 @@ export default Controller.extend(ModalFunctionality, {
|
|||
return this.model.links.filter((link) => !link._destroy);
|
||||
},
|
||||
|
||||
get activeSecondaryLinks() {
|
||||
return this.model.secondaryLinks?.filter((link) => !link._destroy);
|
||||
},
|
||||
|
||||
get header() {
|
||||
return this.model.id
|
||||
? "sidebar.sections.custom.edit"
|
||||
: "sidebar.sections.custom.add";
|
||||
},
|
||||
|
||||
@bind
|
||||
reorder(linkFromId, linkTo, above) {
|
||||
if (linkFromId === linkTo.objectId) {
|
||||
return;
|
||||
}
|
||||
let linkFrom = this.model.links.find(
|
||||
(link) => link.objectId === linkFromId
|
||||
);
|
||||
if (!linkFrom) {
|
||||
linkFrom = this.model.secondaryLinks.find(
|
||||
(link) => link.objectId === linkFromId
|
||||
);
|
||||
}
|
||||
|
||||
if (linkFrom.isPrimary) {
|
||||
this.model.links.removeObject(linkFrom);
|
||||
} else {
|
||||
this.model.secondaryLinks?.removeObject(linkFrom);
|
||||
}
|
||||
|
||||
if (linkTo.isPrimary) {
|
||||
const toPosition = this.model.links.indexOf(linkTo);
|
||||
linkFrom.segment = "primary";
|
||||
this.model.links.insertAt(above ? toPosition : toPosition + 1, linkFrom);
|
||||
} else {
|
||||
linkFrom.segment = "secondary";
|
||||
const toPosition = this.model.secondaryLinks.indexOf(linkTo);
|
||||
this.model.secondaryLinks.insertAt(
|
||||
above ? toPosition : toPosition + 1,
|
||||
linkFrom
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
get canDelete() {
|
||||
return this.model.id && !this.model.sectionType;
|
||||
},
|
||||
|
||||
@bind
|
||||
deleteLink(link) {
|
||||
if (link.id) {
|
||||
link._destroy = "1";
|
||||
} else {
|
||||
if (link.isPrimary) {
|
||||
this.model.links.removeObject(link);
|
||||
} else {
|
||||
this.model.secondaryLinks.removeObject(link);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
addLink() {
|
||||
this.model.links.pushObject(new SectionLink({ router: this.router }));
|
||||
this.nextObjectId = this.nextObjectId + 1;
|
||||
this.model.links.pushObject(
|
||||
new SectionLink({
|
||||
router: this.router,
|
||||
objectId: this.nextObjectId,
|
||||
segment: "primary",
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
deleteLink(link) {
|
||||
if (link.id) {
|
||||
link._destroy = "1";
|
||||
} else {
|
||||
this.model.links.removeObject(link);
|
||||
}
|
||||
addSecondaryLink() {
|
||||
this.nextObjectId = this.nextObjectId + 1;
|
||||
this.model.secondaryLinks.pushObject(
|
||||
new SectionLink({
|
||||
router: this.router,
|
||||
objectId: this.nextObjectId,
|
||||
segment: "secondary",
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
resetToDefault() {
|
||||
return this.dialog.yesNoConfirm({
|
||||
message: I18n.t("sidebar.sections.custom.reset_confirm"),
|
||||
didConfirm: () => {
|
||||
return ajax(`/sidebar_sections/reset/${this.model.id}`, {
|
||||
type: "PUT",
|
||||
})
|
||||
.then((data) => {
|
||||
this.currentUser.sidebar_sections.shiftObject();
|
||||
this.currentUser.sidebar_sections.unshiftObject(
|
||||
data["sidebar_section"]
|
||||
);
|
||||
this.send("closeModal");
|
||||
})
|
||||
.catch((e) =>
|
||||
this.setProperties({
|
||||
flashText: sanitize(extractError(e)),
|
||||
flashClass: "error",
|
||||
})
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
save() {
|
||||
|
|
|
@ -9,6 +9,8 @@ export default class BaseCommunitySectionLink {
|
|||
router,
|
||||
siteSettings,
|
||||
inMoreDrawer,
|
||||
overridenName,
|
||||
overridenIcon,
|
||||
} = {}) {
|
||||
this.router = router;
|
||||
this.topicTrackingState = topicTrackingState;
|
||||
|
@ -16,6 +18,8 @@ export default class BaseCommunitySectionLink {
|
|||
this.appEvents = appEvents;
|
||||
this.siteSettings = siteSettings;
|
||||
this.inMoreDrawer = inMoreDrawer;
|
||||
this.overridenName = overridenName;
|
||||
this.overridenIcon = overridenIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,10 +109,17 @@ export default class BaseCommunitySectionLink {
|
|||
/**
|
||||
* @returns {string} The name of the fontawesome icon to be displayed before the link. Defaults to "link".
|
||||
*/
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "link";
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} The name of the fontawesome icon to be displayed before the link.
|
||||
*/
|
||||
get prefixValue() {
|
||||
return this.overridenIcon || this.defaultPrefixValue;
|
||||
}
|
||||
|
||||
_notImplemented() {
|
||||
throw "not implemented";
|
||||
}
|
||||
|
|
|
@ -16,10 +16,13 @@ export default class AboutSectionLink extends BaseSectionLink {
|
|||
}
|
||||
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.community.links.about.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName.toLowerCase()}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "info-circle";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,17 @@ export default class BadgesSectionLink extends BaseSectionLink {
|
|||
}
|
||||
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.community.links.badges.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName.toLowerCase()}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
|
||||
get shouldDisplay() {
|
||||
return this.siteSettings.enable_badges;
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "certificate";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,10 @@ export default class EverythingSectionLink extends BaseSectionLink {
|
|||
}
|
||||
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.community.links.everything.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName.toLowerCase()}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
|
||||
get currentWhen() {
|
||||
|
@ -92,7 +95,7 @@ export default class EverythingSectionLink extends BaseSectionLink {
|
|||
return "discovery.latest";
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "layer-group";
|
||||
}
|
||||
|
||||
|
|
|
@ -20,10 +20,13 @@ export default class FAQSectionLink extends BaseSectionLink {
|
|||
}
|
||||
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.community.links.faq.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName.toLowerCase()}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "question-circle";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,17 @@ export default class GroupsSectionLink extends BaseSectionLink {
|
|||
}
|
||||
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.community.links.groups.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName.toLowerCase()}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
|
||||
get shouldDisplay() {
|
||||
return this.siteSettings.enable_group_directory;
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "user-friends";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,10 @@ export default class UsersSectionLink extends BaseSectionLink {
|
|||
}
|
||||
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.community.links.users.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName.toLowerCase()}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
|
||||
get shouldDisplay() {
|
||||
|
@ -26,7 +29,7 @@ export default class UsersSectionLink extends BaseSectionLink {
|
|||
);
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "users";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
customSectionLinks,
|
||||
secondaryCustomSectionLinks,
|
||||
} from "discourse/lib/sidebar/custom-community-section-links";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
||||
const LINKS_IN_BOTH_SEGMENTS = ["/review"];
|
||||
|
||||
|
@ -111,13 +112,23 @@ export default class CommunitySection {
|
|||
const sectionLinkClass = SPECIAL_LINKS_MAP[link.value];
|
||||
|
||||
if (sectionLinkClass) {
|
||||
return this.#initializeSectionLink(sectionLinkClass, inMoreDrawer);
|
||||
return this.#initializeSectionLink(
|
||||
sectionLinkClass,
|
||||
inMoreDrawer,
|
||||
link.name,
|
||||
link.scon
|
||||
);
|
||||
} else {
|
||||
return new SectionLink(link, this, this.router);
|
||||
}
|
||||
}
|
||||
|
||||
#initializeSectionLink(sectionLinkClass, inMoreDrawer) {
|
||||
#initializeSectionLink(
|
||||
sectionLinkClass,
|
||||
inMoreDrawer,
|
||||
overridenName,
|
||||
overridenIcon
|
||||
) {
|
||||
if (this.router.isDestroying) {
|
||||
return;
|
||||
}
|
||||
|
@ -128,28 +139,48 @@ export default class CommunitySection {
|
|||
router: this.router,
|
||||
siteSettings: this.siteSettings,
|
||||
inMoreDrawer,
|
||||
overridenName,
|
||||
overridenIcon,
|
||||
});
|
||||
}
|
||||
|
||||
get decoratedTitle() {
|
||||
return I18n.t(
|
||||
`sidebar.sections.${this.section.title.toLowerCase()}.header_link_text`
|
||||
`sidebar.sections.${this.section.title.toLowerCase()}.header_link_text`,
|
||||
{ defaultValue: this.section.title }
|
||||
);
|
||||
}
|
||||
|
||||
get headerActions() {
|
||||
if (this.currentUser?.admin) {
|
||||
return [
|
||||
{
|
||||
action: this.editSection,
|
||||
title: I18n.t(
|
||||
"sidebar.sections.community.header_action_edit_section_title"
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
if (this.currentUser) {
|
||||
return [
|
||||
{
|
||||
action: this.composeTopic,
|
||||
title: I18n.t("sidebar.sections.community.header_action_title"),
|
||||
title: I18n.t(
|
||||
"sidebar.sections.community.header_action_create_topic_title"
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
get headerActionIcon() {
|
||||
return "plus";
|
||||
return this.currentUser?.admin ? "pencil-alt" : "plus";
|
||||
}
|
||||
|
||||
@action
|
||||
editSection() {
|
||||
showModal("sidebar-section-form", { model: this.section });
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
@ -16,14 +16,17 @@ export default class AdminSectionLink extends BaseSectionLink {
|
|||
}
|
||||
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.community.links.admin.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName.toLowerCase()}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
|
||||
get shouldDisplay() {
|
||||
return this.currentUser?.staff;
|
||||
return !!this.currentUser?.staff;
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "wrench";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,12 @@ export default class MyPostsSectionLink extends BaseSectionLink {
|
|||
if (this._hasDraft && this.currentUser?.new_new_view_enabled) {
|
||||
return I18n.t("sidebar.sections.community.links.my_posts.content_drafts");
|
||||
} else {
|
||||
return I18n.t("sidebar.sections.community.links.my_posts.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName
|
||||
.toLowerCase()
|
||||
.replace(" ", "/")}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +95,7 @@ export default class MyPostsSectionLink extends BaseSectionLink {
|
|||
return this.draftCount > 0;
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
if (this._hasDraft && this.currentUser?.new_new_view_enabled) {
|
||||
return "pencil-alt";
|
||||
}
|
||||
|
|
|
@ -53,7 +53,10 @@ export default class ReviewSectionLink extends BaseSectionLink {
|
|||
}
|
||||
|
||||
get text() {
|
||||
return I18n.t("sidebar.sections.community.links.review.content");
|
||||
return I18n.t(
|
||||
`sidebar.sections.community.links.${this.overridenName.toLowerCase()}.content`,
|
||||
{ defaultValue: this.overridenName }
|
||||
);
|
||||
}
|
||||
|
||||
get shouldDisplay() {
|
||||
|
@ -70,7 +73,7 @@ export default class ReviewSectionLink extends BaseSectionLink {
|
|||
}
|
||||
}
|
||||
|
||||
get prefixValue() {
|
||||
get defaultPrefixValue() {
|
||||
return "flag";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,67 +22,23 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#each this.activeLinks as |link|}}
|
||||
<div class="row-wrapper">
|
||||
<div class="input-group">
|
||||
<label for="link-name">{{i18n
|
||||
"sidebar.sections.custom.links.icon.label"
|
||||
}}</label>
|
||||
<IconPicker
|
||||
@name="icon"
|
||||
@value={{link.icon}}
|
||||
@options={{hash maximum=1}}
|
||||
class={{link.iconCssClass}}
|
||||
@onlyAvailable={{true}}
|
||||
@onChange={{action (mut link.icon)}}
|
||||
/>
|
||||
{{#if link.invalidIconMessage}}
|
||||
<div class="icon warning">
|
||||
{{link.invalidIconMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="link-name">{{i18n
|
||||
"sidebar.sections.custom.links.name.label"
|
||||
}}</label>
|
||||
<Input
|
||||
name="link-name"
|
||||
@type="text"
|
||||
@value={{link.name}}
|
||||
class={{link.nameCssClass}}
|
||||
{{on "input" (action (mut link.name) value="target.value")}}
|
||||
/>
|
||||
{{#if link.invalidNameMessage}}
|
||||
<div class="name warning">
|
||||
{{link.invalidNameMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="link-url">{{i18n
|
||||
"sidebar.sections.custom.links.value.label"
|
||||
}}</label>
|
||||
<Input
|
||||
name="link-url"
|
||||
@type="text"
|
||||
@value={{link.value}}
|
||||
class={{link.valueCssClass}}
|
||||
{{on "input" (action (mut link.value) value="target.value")}}
|
||||
/>
|
||||
{{#if link.invalidValueMessage}}
|
||||
<div class="value warning">
|
||||
{{link.invalidValueMessage}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<DButton
|
||||
@icon="trash-alt"
|
||||
@action={{action "deleteLink" link}}
|
||||
@class="btn-flat delete-link"
|
||||
@title="sidebar.sections.custom.links.delete"
|
||||
/>
|
||||
<div class="row-wrapper header">
|
||||
<div class="input-group link-icon">
|
||||
<label>{{i18n "sidebar.sections.custom.links.icon.label"}}</label>
|
||||
</div>
|
||||
<div class="input-group link-name">
|
||||
<label>{{i18n "sidebar.sections.custom.links.name.label"}}</label>
|
||||
</div>
|
||||
<div class="input-group link-url">
|
||||
<label>{{i18n "sidebar.sections.custom.links.value.label"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
{{#each this.activeLinks as |link|}}
|
||||
<Sidebar::SectionFormLink
|
||||
@link={{link}}
|
||||
@deleteLink={{this.deleteLink}}
|
||||
@reorderCallback={{this.reorder}}
|
||||
/>
|
||||
{{/each}}
|
||||
<DButton
|
||||
@action={{action "addLink"}}
|
||||
|
@ -90,9 +46,40 @@
|
|||
@title="sidebar.sections.custom.links.add"
|
||||
@icon="plus"
|
||||
@label="sidebar.sections.custom.links.add"
|
||||
@ariaLabel="sidebar.sections.custom.links.add"
|
||||
/>
|
||||
{{#if this.currentUser.staff}}
|
||||
<div class="row-wrapper">
|
||||
{{#if this.model.sectionType}}
|
||||
<hr />
|
||||
<h3>{{i18n "sidebar.sections.custom.more_menu"}}</h3>
|
||||
{{#each this.activeSecondaryLinks as |link|}}
|
||||
<Sidebar::SectionFormLink
|
||||
@link={{link}}
|
||||
@deleteLink={{this.deleteLink}}
|
||||
@reorderCallback={{this.reorder}}
|
||||
/>
|
||||
{{/each}}
|
||||
<DButton
|
||||
@action={{action "addSecondaryLink"}}
|
||||
@class="btn-flat btn-text add-link"
|
||||
@title="sidebar.sections.custom.links.add"
|
||||
@icon="plus"
|
||||
@label="sidebar.sections.custom.links.add"
|
||||
@ariaLabel="sidebar.sections.custom.links.add"
|
||||
/>
|
||||
|
||||
{{#if this.model.sectionType}}
|
||||
<DButton
|
||||
@action={{action "resetToDefault"}}
|
||||
@class="btn-flat btn-text reset-link"
|
||||
@icon="undo"
|
||||
@title="sidebar.sections.custom.links.reset"
|
||||
@label="sidebar.sections.custom.links.reset"
|
||||
@ariaLabel="sidebar.sections.custom.links.reset"
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#if (and this.currentUser.staff (not this.model.sectionType))}}
|
||||
<div class="row-wrapper mark-public-wrapper">
|
||||
<label class="checkbox-label">
|
||||
<Input
|
||||
@type="checkbox"
|
||||
|
@ -112,15 +99,17 @@
|
|||
@action={{action "save"}}
|
||||
@class="btn-primary"
|
||||
@label="sidebar.sections.custom.save"
|
||||
@ariaLabel="sidebar.sections.custom.save"
|
||||
@disabled={{not this.model.valid}}
|
||||
/>
|
||||
{{#if this.model.id}}
|
||||
{{#if this.canDelete}}
|
||||
<DButton
|
||||
@icon="trash-alt"
|
||||
@id="delete-section"
|
||||
@class="btn-danger delete"
|
||||
@action={{action "delete"}}
|
||||
@label="sidebar.sections.custom.delete"
|
||||
@ariaLabel="sidebar.sections.custom.delete"
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -25,6 +25,7 @@ acceptance("Sidebar - Logged on user - Community Section", function (needs) {
|
|||
tracked_tags: ["tag1"],
|
||||
watched_tags: ["tag2"],
|
||||
watching_first_post_tags: ["tag3"],
|
||||
admin: false,
|
||||
});
|
||||
|
||||
needs.settings({
|
||||
|
|
|
@ -75,7 +75,7 @@ export default {
|
|||
},
|
||||
{
|
||||
id: 331,
|
||||
name: "Info",
|
||||
name: "About",
|
||||
value: "/about",
|
||||
icon: "info-circle",
|
||||
external: false,
|
||||
|
|
|
@ -723,7 +723,7 @@ export default {
|
|||
},
|
||||
{
|
||||
id: 331,
|
||||
name: "Info",
|
||||
name: "About",
|
||||
value: "/about",
|
||||
icon: "info-circle",
|
||||
external: false,
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
.sidebar-section-link-prefix.icon {
|
||||
cursor: move;
|
||||
}
|
||||
.sidebar-section[data-section-name="community"]
|
||||
.sidebar-section-link-prefix.icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a {
|
||||
-webkit-touch-callout: none !important;
|
||||
|
|
|
@ -106,8 +106,21 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-section-form-modal {
|
||||
.draggable {
|
||||
cursor: move;
|
||||
align-self: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
-webkit-user-drag: element;
|
||||
-khtml-user-drag: element;
|
||||
-moz-user-drag: element;
|
||||
-o-user-drag: element;
|
||||
user-drag: element;
|
||||
}
|
||||
.dragging {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.modal-inner-container {
|
||||
width: var(--modal-max-width);
|
||||
}
|
||||
|
@ -122,17 +135,52 @@
|
|||
}
|
||||
.row-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto 2em;
|
||||
grid-template-columns: 25px 60px auto auto 2em;
|
||||
gap: 1em;
|
||||
margin-top: 1em;
|
||||
padding: 0.5em 1px;
|
||||
-webkit-user-drag: none;
|
||||
-khtml-user-drag: none;
|
||||
-moz-user-drag: none;
|
||||
-o-user-drag: none;
|
||||
user-drag: none;
|
||||
cursor: default;
|
||||
|
||||
&.header {
|
||||
padding-bottom: 0;
|
||||
padding-top: 1em;
|
||||
label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.link-url {
|
||||
margin-left: -1em;
|
||||
}
|
||||
}
|
||||
|
||||
&.drag-above {
|
||||
border-top: 1px dotted #666;
|
||||
margin-top: -1px;
|
||||
}
|
||||
&.drag-below {
|
||||
border-bottom: 1px dotted #666;
|
||||
padding-bottom: calc(0.5em - 1px);
|
||||
}
|
||||
.link-icon {
|
||||
grid-column: 1 / span 2;
|
||||
padding-left: calc(25px + 1em);
|
||||
}
|
||||
&.mark-public-wrapper {
|
||||
label {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.delete-link {
|
||||
height: 1em;
|
||||
align-self: end;
|
||||
margin-bottom: 0.75em;
|
||||
align-self: center;
|
||||
margin-right: 1em;
|
||||
}
|
||||
.btn-flat.add-link {
|
||||
.btn-flat.add-link,
|
||||
.btn-flat.reset-link {
|
||||
margin-top: 1em;
|
||||
margin-left: -0.65em;
|
||||
&:active,
|
||||
|
@ -148,6 +196,9 @@
|
|||
color: var(--tertiary-hover);
|
||||
}
|
||||
}
|
||||
.btn-flat.reset-link {
|
||||
float: right;
|
||||
}
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -156,4 +207,13 @@
|
|||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
.select-kit.multi-select .multi-select-header .formatted-selection {
|
||||
display: none;
|
||||
}
|
||||
.modal-inner-container .select-kit {
|
||||
width: 60px;
|
||||
}
|
||||
.select-kit.is-expanded .select-kit-body {
|
||||
width: 220px !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ class SidebarSectionsController < ApplicationController
|
|||
Site.clear_anon_cache!
|
||||
end
|
||||
|
||||
render_serialized(sidebar_section, SidebarSectionSerializer)
|
||||
render_serialized(sidebar_section.reload, SidebarSectionSerializer)
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
render_json_error(e.record.errors.full_messages.first)
|
||||
rescue Discourse::InvalidAccess
|
||||
|
|
|
@ -4413,10 +4413,13 @@ en:
|
|||
save: "Save"
|
||||
delete: "Delete"
|
||||
delete_confirm: "Are you sure you want to delete this section?"
|
||||
reset_confirm: "Are you sure you want to reset this section to default?"
|
||||
public: "Make this section public and visible to everyone"
|
||||
more_menu: "More menu"
|
||||
links:
|
||||
add: "Add another link"
|
||||
delete: "Delete link"
|
||||
reset: "Reset to default"
|
||||
icon:
|
||||
label: "Icon"
|
||||
validation:
|
||||
|
@ -4473,7 +4476,8 @@ en:
|
|||
configure_defaults: "Configure defaults"
|
||||
community:
|
||||
header_link_text: "Community"
|
||||
header_action_title: "Create a topic"
|
||||
header_action_create_topic_title: "Create a topic"
|
||||
header_action_edit_section_title: "Edit Community section"
|
||||
links:
|
||||
about:
|
||||
content: "About"
|
||||
|
|
|
@ -114,25 +114,17 @@ describe "Custom sidebar sections", type: :system, js: true do
|
|||
sign_in user
|
||||
visit("/latest")
|
||||
|
||||
within("[data-section-name='my-section'] .sidebar-section-link-wrapper:nth-child(1)") do
|
||||
expect(sidebar).to have_section_link("Sidebar Tags")
|
||||
end
|
||||
|
||||
within("[data-section-name='my-section'] .sidebar-section-link-wrapper:nth-child(2)") do
|
||||
expect(sidebar).to have_section_link("Sidebar Categories")
|
||||
end
|
||||
expect(sidebar.primary_section_links("my-section")).to eq(
|
||||
["Sidebar Tags", "Sidebar Categories"],
|
||||
)
|
||||
|
||||
tags_link = find(".sidebar-section-link[data-link-name='Sidebar Tags']")
|
||||
categories_link = find(".sidebar-section-link[data-link-name='Sidebar Categories']")
|
||||
tags_link.drag_to(categories_link, html5: true, delay: 0.4)
|
||||
|
||||
within("[data-section-name='my-section'] .sidebar-section-link-wrapper:nth-child(1)") do
|
||||
expect(sidebar).to have_section_link("Sidebar Categories")
|
||||
end
|
||||
|
||||
within("[data-section-name='my-section'] .sidebar-section-link-wrapper:nth-child(2)") do
|
||||
expect(sidebar).to have_section_link("Sidebar Tags")
|
||||
end
|
||||
expect(sidebar.primary_section_links("my-section")).to eq(
|
||||
["Sidebar Categories", "Sidebar Tags"],
|
||||
)
|
||||
end
|
||||
|
||||
it "does not allow the user to edit public section" do
|
||||
|
@ -201,6 +193,29 @@ describe "Custom sidebar sections", type: :system, js: true do
|
|||
expect(sidebar).to have_no_section("Edited public section")
|
||||
end
|
||||
|
||||
it "allows admin to edit community section and reset to default" do
|
||||
sign_in admin
|
||||
visit("/latest")
|
||||
|
||||
sidebar.edit_custom_section("Community")
|
||||
section_modal.fill_name("Edited community section")
|
||||
section_modal.everything_link.drag_to(section_modal.review_link, delay: 0.4)
|
||||
section_modal.save
|
||||
|
||||
expect(sidebar).to have_section("Edited community section")
|
||||
expect(sidebar.primary_section_links("edited-community-section")).to eq(
|
||||
["My Posts", "Everything", "Admin", "More"],
|
||||
)
|
||||
|
||||
sidebar.edit_custom_section("Edited community section")
|
||||
section_modal.reset
|
||||
|
||||
expect(sidebar).to have_section("Community")
|
||||
expect(sidebar.primary_section_links("community")).to eq(
|
||||
["Everything", "My Posts", "Admin", "More"],
|
||||
)
|
||||
end
|
||||
|
||||
it "shows anonymous public sections" do
|
||||
sidebar_section = Fabricate(:sidebar_section, title: "Public section", public: true)
|
||||
sidebar_url_1 = Fabricate(:sidebar_url, name: "Sidebar Tags", value: "/tags")
|
||||
|
|
|
@ -63,6 +63,10 @@ module PageObjects
|
|||
find(SIDEBAR_WRAPPER_SELECTOR).has_no_button?(name)
|
||||
end
|
||||
|
||||
def primary_section_links(slug)
|
||||
all("[data-section-name='#{slug}'] .sidebar-section-link-wrapper").map(&:text)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def section_link_present?(name, href: nil, active: false, present:)
|
||||
|
|
|
@ -25,7 +25,7 @@ module PageObjects
|
|||
|
||||
def click_community_header_button
|
||||
page.click_button(
|
||||
I18n.t("js.sidebar.sections.community.header_action_title"),
|
||||
I18n.t("js.sidebar.sections.community.header_action_create_topic_title"),
|
||||
class: "sidebar-section-header-button",
|
||||
)
|
||||
end
|
||||
|
|
|
@ -28,6 +28,11 @@ module PageObjects
|
|||
find(".dialog-container .btn-primary").click
|
||||
end
|
||||
|
||||
def reset
|
||||
find(".reset-link").click
|
||||
find(".dialog-footer .btn-primary").click
|
||||
end
|
||||
|
||||
def save
|
||||
find("#save-section").click
|
||||
end
|
||||
|
@ -39,9 +44,18 @@ module PageObjects
|
|||
def has_disabled_save?
|
||||
find_button("Save", disabled: true)
|
||||
end
|
||||
|
||||
def has_enabled_save?
|
||||
find_button("Save", disabled: false)
|
||||
end
|
||||
|
||||
def everything_link
|
||||
find(".draggable[data-link-name='Everything']")
|
||||
end
|
||||
|
||||
def review_link
|
||||
find(".draggable[data-link-name='Review']")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue