Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use shoelace switch for Element publish toggle #2633

Merged
merged 4 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions app/assets/stylesheets/alchemy/_custom-properties.scss
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,17 @@
--color-grey-blue_medium: hsl(224deg, 23%, 40%);
--color-grey-blue_dark: hsl(224deg, 23%, 26%);

--color-grey_light: hsl(0deg, 0%, 97%);
--color-grey_medium: hsl(0deg, 0%, 94%);
--color-grey_light: hsl(0deg, 0%, 94%);
--color-grey_medium: hsl(0deg, 0%, 78%);
--color-grey_dark: hsl(0deg, 0%, 40%);
--color-grey_very_dark: hsl(0deg, 0%, 20%);
--color-text: hsla(224, 22.7%, 25.9%, 0.8);
}

.alchemy-light {
--outline-color: var(--color-orange_dark);

--tabs_indicator-color: var(--color-orange_dark);
--tabs_track-color: var(--color-grey_medium);
--tabs_track-color: var(--color-grey_light);
--sl-input-label-color: var(--color-text);
}
38 changes: 23 additions & 15 deletions app/assets/stylesheets/alchemy/elements.scss
Original file line number Diff line number Diff line change
Expand Up @@ -136,26 +136,14 @@ alchemy-tinymce {
&.hidden {
display: block;
border-style: dashed;
opacity: 0.5;
transition: opacity $transition-duration;

> .element-header {
background-color: transparent;
background-image: linear-gradient(
45deg,
$light-gray 25%,
$medium-gray 25%,
$medium-gray 50%,
$light-gray 50%,
$light-gray 75%,
$medium-gray 75%,
$medium-gray 100%
);
background-size: 28.28px 28.28px;
}

&:hover {
opacity: 1;
.element-hidden-icon {
display: inline;
}
}
}

Expand Down Expand Up @@ -258,6 +246,10 @@ alchemy-tinymce {
border-bottom: $default-border;
background-color: $light-gray;
transition: all $transition-duration;

sl-switch::part(label) {
display: none;
}
}

.element-header:hover + .element-toolbar,
Expand Down Expand Up @@ -398,6 +390,10 @@ alchemy-tinymce {
}
}

.element-hidden-icon {
display: none;
}

.element-toolbar {
display: flex;
width: 100%;
Expand All @@ -407,6 +403,18 @@ alchemy-tinymce {
border-bottom: 1px solid $medium-gray;
}

alchemy-publish-element-button {
display: inline-flex;
align-items: center;
margin-left: auto;
margin-right: $default-margin;

sl-switch {
--thumb-size: 10px;
--height: 12px;
}
}

.element-footer {
border-top: 1px solid $medium-gray;
padding: 2 * $default-padding;
Expand Down
22 changes: 11 additions & 11 deletions app/assets/stylesheets/alchemy/shoelace.scss
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,17 @@
--sl-color-danger-950: var(--color-red_dark);

/* Neutral */
--sl-color-neutral-50: var(--color-grey-blue_light);
--sl-color-neutral-100: var(--color-grey-blue_light);
--sl-color-neutral-200: var(--color-grey-blue_light);
--sl-color-neutral-300: var(--color-grey-blue_light);
--sl-color-neutral-400: var(--color-grey-blue_medium);
--sl-color-neutral-500: var(--color-grey-blue_medium);
--sl-color-neutral-600: var(--color-grey-blue_dark);
--sl-color-neutral-700: var(--color-grey-blue_dark);
--sl-color-neutral-800: var(--color-grey-blue_dark);
--sl-color-neutral-900: var(--color-grey-blue_dark);
--sl-color-neutral-950: var(--color-grey-blue_dark);
--sl-color-neutral-50: var(--color-grey_light);
--sl-color-neutral-100: var(--color-grey_light);
--sl-color-neutral-200: var(--color-grey_light);
--sl-color-neutral-300: var(--color-grey_medium);
--sl-color-neutral-400: var(--color-grey_medium);
--sl-color-neutral-500: var(--color-grey_medium);
--sl-color-neutral-600: var(--color-grey_dark);
--sl-color-neutral-700: var(--color-grey_dark);
--sl-color-neutral-800: var(--color-grey_very_dark);
--sl-color-neutral-900: var(--color-grey-very_dark);
--sl-color-neutral-950: var(--color-grey-very_dark);

/* Neutral one-offs */
--sl-color-neutral-0: hsl(0, 0%, 100%);
Expand Down
7 changes: 6 additions & 1 deletion app/controllers/alchemy/admin/elements_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ def destroy
end

def publish
@element.update(public: !@element.public?)
@element.public = !@element.public?
@element.save(validate: false)
render json: {
public: @element.public?,
label: @element.public? ? Alchemy.t(:hide_element) : Alchemy.t(:show_element)
}
end

def order
Expand Down
1 change: 1 addition & 0 deletions app/javascript/alchemy_admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import "alchemy_admin/components/select"
import "alchemy_admin/components/spinner"
import "alchemy_admin/components/tinymce"
import "alchemy_admin/components/tooltip"
import "@shoelace/switch"
import "@shoelace/tab"
import "@shoelace/tab-group"
import "@shoelace/tab-panel"
Expand Down
22 changes: 22 additions & 0 deletions app/javascript/alchemy_admin/components/element_editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import IngredientAnchorLink from "alchemy_admin/ingredient_anchor_link"
import { post } from "alchemy_admin/utils/ajax"
import { createHtmlElement } from "../utils/dom_helpers"

import "./element_editor/publish_element_button"

export class ElementEditor extends HTMLElement {
constructor() {
super()
Expand Down Expand Up @@ -356,6 +358,26 @@ export class ElementEditor extends HTMLElement {
)
}

/**
* Sets element published or hidden
* @param {boolean}
*/
set published(isPublished) {
if (isPublished) {
this.classList.remove("hidden")
} else {
this.classList.add("hidden")
}
}

/**
* Is element published or hidden
* @returns {boolean}
*/
get published() {
return !this.classList.contains("hidden")
}

/**
* @returns {boolean}
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { patch } from "alchemy_admin/utils/ajax"

export class PublishElementButton extends HTMLElement {
constructor() {
super()

this.addEventListener("sl-change", this)
}

handleEvent(event) {
const elementEditor = event.target.closest("alchemy-element-editor")
if (elementEditor === this.elementEditor) {
patch(Alchemy.routes.publish_admin_element_path(this.elementId))
.then((response) => {
this.elementEditor.published = response.data.public
this.label.innerText = response.data.label
Alchemy.reloadPreview()
})
.catch((error) => Alchemy.growl(error.message, "error"))
}
}

get elementEditor() {
return this.closest("alchemy-element-editor")
}

get label() {
return this.querySelector("label")
}

get elementId() {
return this.elementEditor.elementId
}
}

customElements.define("alchemy-publish-element-button", PublishElementButton)
4 changes: 4 additions & 0 deletions app/views/alchemy/admin/elements/_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
<%= sanitize(element.preview_text.presence || '&nbsp;') %>
</span>
</span>
<span class="element-hidden-icon">
<%= render_icon("cloud-off", size: "1x") %>
<%= Alchemy.t(:element_hidden) %>
</span>
<%= button_tag({
title: Alchemy.t(element.folded? ? :show_element_content : :hide_element_content),
class: "element-toggle"
Expand Down
20 changes: 6 additions & 14 deletions app/views/alchemy/admin/elements/_toolbar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,10 @@
) -%>
<label><%= Alchemy.t("Delete element") %></label>
</div>
<div class="button_with_label publish-element-button">
<%= link_to(
render_icon(:toggle, style: element.public? ? 'fill' : 'line'),
alchemy.publish_admin_element_path(id: element.id),
remote: true,
method: :patch,
class: "icon_button"
) %>
<% if element.public? %>
<label><%= Alchemy.t(:hide_element) %></label>
<% else %>
<label><%= Alchemy.t(:show_element) %></label>
<% end %>
</div>
<alchemy-publish-element-button class="button_with_label">
<sl-switch <%= element.public? ? nil : "checked" %> size="small">
<%= t(".hide") %>
</sl-switch>
<label class="left"><%= Alchemy.t(:hide_element) %></label>
</alchemy-publish-element-button>
</div>
17 changes: 0 additions & 17 deletions app/views/alchemy/admin/elements/publish.js.erb

This file was deleted.

4 changes: 4 additions & 0 deletions app/views/alchemy/admin/partials/_routes.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
return '<%= alchemy.expand_admin_element_path(id: 1) %>'.replace(/1/, id);
},

publish_admin_element_path: function(id) {
return '<%= alchemy.publish_admin_element_path(id: 1) %>'.replace(/1/, id);
},

node: {
toggle_folded_api_path: function(id) {
return '<%= alchemy.toggle_folded_api_node_path(id: 1) %>'.replace(/1/, id);
Expand Down
1 change: 1 addition & 0 deletions config/importmap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
pin "lodash-es/max", to: "https://ga.jspm.io/npm:lodash-es@4.17.21/max.js", preload: true
pin "sortablejs", to: "https://ga.jspm.io/npm:sortablejs@1.15.0/modular/sortable.esm.js", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@shoelace/switch", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/switch/switch.js", preload: true
pin "@shoelace/tab", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/tab/tab.js", preload: true
pin "@shoelace/tab-group", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/tab-group/tab-group.js", preload: true
pin "@shoelace/tab-panel", to: "https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.9.0/cdn/components/tab-panel/tab-panel.js", preload: true
Expand Down
5 changes: 5 additions & 0 deletions config/locales/alchemy.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,10 @@ en:
host: Either enter a <a href="https://en.wikipedia.org/wiki/Fully_qualified_domain_name" target="_blank">FQDN</a> or use a asterisk (*) to allow all domains to access this site.
aliases: Enter additional domains this site should be accessible by. Please separate them by space or new line.

admin:
elements:
toolbar:
hide: Hide
add_nested_element: "Add %{name}"
anchor: "Anchor"
anchor_link_headline: You can link to an anchor from the current page.
Expand Down Expand Up @@ -420,6 +424,7 @@ en:
edit_tag: "Edit tag"
edit_selected_pictures: "Edit selected pictures"
element_editor_not_found: "Error within this Element"
element_hidden: "Hidden"
element_of_type: "Element"
element_saved: "Saved element."
enter_external_link: "Please enter the URL you want to link with"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import "alchemy_admin/components/element_editor/publish_element_button"
import { renderComponent } from "../component.helper"

jest.mock("alchemy_admin/utils/ajax", () => {
return {
__esModule: true,
patch(url) {
return new Promise((resolve, reject) => {
switch (url) {
case "/admin/elements/123/publish":
resolve({
data: {
public: false,
label: "Hide element"
}
})
break
case "/admin/elements/666/publish":
reject(new Error("Something went wrong!"))
break
default:
reject(new Error(`URL ${url} not found!`))
}
})
}
}
})

describe("alchemy-publish-element-button", () => {
let html = `
<alchemy-element-editor>
<div class="element-toolbar">
<alchemy-publish-element-button>
<label>Show element</label>
</alchemy-publish-element-button>
</div>
</alchemy-element-editor>
`
let button

beforeEach(() => {
button = renderComponent("alchemy-publish-element-button", html)
Alchemy = {
routes: {
publish_admin_element_path(id) {
return `/admin/elements/${id}/publish`
}
},
reloadPreview: jest.fn(),
growl: jest.fn()
}

Alchemy.reloadPreview.mockClear()
Alchemy.growl.mockClear()
})

describe("on change", () => {
it("Publishes element editor", () => {
const change = new CustomEvent("sl-change", { bubbles: true })
jest.spyOn(button, "elementId", "get").mockReturnValue("123")
button.dispatchEvent(change)

return new Promise((resolve) => {
setTimeout(() => {
expect(button.label.innerText).toEqual("Hide element")
expect(Alchemy.reloadPreview).toHaveBeenCalled()
resolve()
}, 1)
})
}, 100)
})

describe("on error", () => {
it("Shows error", () => {
const change = new CustomEvent("sl-change", { bubbles: true })
jest.spyOn(button, "elementId", "get").mockReturnValue("666")
button.dispatchEvent(change)

return new Promise((resolve) => {
setTimeout(() => {
expect(Alchemy.growl).toHaveBeenCalled()
resolve()
}, 1)
})
}, 100)
})
})
Loading
Loading