diff --git a/CHANGES.md b/CHANGES.md index d1ca253de6..6913968dfe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ ### :heart: Community contributions (Thank you!) ### :sparkles: New features & Enhancements +- Show current Penpot version [Taiga #11603](https://tree.taiga.io/project/penpot/us/11603) ### :bug: Bugs fixed diff --git a/frontend/playwright/ui/pages/DashboardPage.js b/frontend/playwright/ui/pages/DashboardPage.js index f297218d3e..5967d0e74e 100644 --- a/frontend/playwright/ui/pages/DashboardPage.js +++ b/frontend/playwright/ui/pages/DashboardPage.js @@ -288,6 +288,14 @@ export class DashboardPage extends BaseWebSocketPage { ); await expect(this.mainHeading).toHaveText("Libraries"); } + + async openProfileMenu() { + await this.userAccount.click(); + } + + async clickProfileMenuItem(menuSection) { + await this.sidebarMenu.getByText(menuSection).click(); + } } export default DashboardPage; diff --git a/frontend/playwright/ui/specs/profile-menu.spec.js b/frontend/playwright/ui/specs/profile-menu.spec.js new file mode 100644 index 0000000000..f804e5d26f --- /dev/null +++ b/frontend/playwright/ui/specs/profile-menu.spec.js @@ -0,0 +1,44 @@ +import { test, expect } from "@playwright/test"; +import DashboardPage from "../pages/DashboardPage"; + +test.beforeEach(async ({ page }) => { + await DashboardPage.init(page); + await DashboardPage.mockRPC( + page, + "get-profile", + "logged-in-user/get-profile-logged-in-no-onboarding.json", + ); +}); + +test("Navigate to penpot changelog from profile menu", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + + await dashboardPage.openProfileMenu(); + await dashboardPage.clickProfileMenuItem("About Penpot"); + + // Listen for the new page (tab) that opens when clicking "Penpot Changelog" + const [newPage] = await Promise.all([ + page.context().waitForEvent("page"), + dashboardPage.clickProfileMenuItem("Penpot Changelog"), + ]); + + await newPage.waitForLoadState(); + await expect(newPage).toHaveURL("https://github.com/penpot/penpot/blob/develop/CHANGES.md"); +}); + +test("Opens release notes from current version from profile menu", async ({ page }) => { + const dashboardPage = new DashboardPage(page); + await dashboardPage.goToDashboard(); + + await dashboardPage.openProfileMenu(); + await dashboardPage.clickProfileMenuItem("About Penpot"); + await expect( + page.getByText("Version 0.0.0 notes"), + ).toBeVisible(); + await dashboardPage.clickProfileMenuItem("Version"); + await expect( + page.getByText("new in penpot?"), + ).toBeVisible(); + +}); diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index d4a071709e..abf279cc67 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -33,6 +33,7 @@ show-subscription-dashboard-banner? get-subscription-type]] [app.main.ui.dashboard.team-form] + [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.icons :as deprecated-icon] [app.util.dom :as dom] [app.util.dom.dnd :as dnd] @@ -762,10 +763,139 @@ [:span {:class (stl/css :empty-text)} (tr "dashboard.no-projects-placeholder")]])]] [:div {:class (stl/css-case :separator true :overflow-separator overflow?)}]])) +(mf/defc help-learning-menu* + {::mf/props :obj + ::mf/private true} + [{:keys [on-close on-click]}] + (let [handle-click-url + (mf/use-fn + (fn [event] + (let [url (-> (dom/get-current-target event) + (dom/get-data "url")) + eventname (-> (dom/get-current-target event) + (dom/get-data "eventname"))] + (st/emit! (ptk/event ::ev/event {::ev/name eventname + ::ev/origin "menu:in-app"})) + (dom/open-new-window url)))) + + handle-feedback-click + (mf/use-fn #(on-click :settings-feedback %))] + + [:> dropdown-menu* {:show true + :class (stl/css :sub-menu :help-learning) + :on-close on-close} + + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :data-url "https://help.penpot.app" + :on-click handle-click-url + :data-eventname "explore-help-center-click"} + (tr "labels.help-center")] + + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :data-url "https://penpot.app/learning-center" + :on-click handle-click-url + :data-eventname "explore-learning-center-click"} + (tr "labels.learning-center")] + + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :data-url "https://penpot.app/penpothub" + :on-click handle-click-url + :data-eventname "explore-penpot-hub-click"} + (tr "labels.penpot-hub")] + + (when (contains? cf/flags :user-feedback) + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :on-click handle-feedback-click} + (tr "labels.give-feedback")])])) + +(mf/defc community-contributions-menu* + {::mf/props :obj + ::mf/private true} + [{:keys [on-close]}] + (let [handle-click-url + (mf/use-fn + (fn [event] + (let [url (-> (dom/get-current-target event) + (dom/get-data "url")) + eventname (-> (dom/get-current-target event) + (dom/get-data "eventname"))] + (st/emit! (ptk/event ::ev/event {::ev/name eventname + ::ev/origin "menu:in-app"})) + (dom/open-new-window url))))] + + [:> dropdown-menu* {:show true + :class (stl/css :sub-menu :community) + :on-close on-close} + + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :data-url "https://github.com/penpot/penpot" + :on-click handle-click-url + :data-eventname "explore-github-repository-click"} + (tr "labels.github-repo")] + + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :data-url "https://community.penpot.app" + :on-click handle-click-url + :data-eventname "explore-community-click"} + (tr "labels.community")]])) + +(mf/defc about-penpot-menu* + {::mf/props :obj + ::mf/private true} + [{:keys [on-close]}] + (let [version cf/version + show-release-notes + (mf/use-fn + (fn [event] + (st/emit! (ptk/event ::ev/event {::ev/name "show-release-notes" :version (:main version)})) + (if (and (kbd/alt? event) (kbd/mod? event)) + (st/emit! (modal/show {:type :onboarding})) + (st/emit! (modal/show {:type :release-notes :version (:main version)}))))) + + handle-click-url + (mf/use-fn + (fn [event] + (let [url (-> (dom/get-current-target event) + (dom/get-data "url")) + eventname (-> (dom/get-current-target event) + (dom/get-data "eventname"))] + (st/emit! (ptk/event ::ev/event {::ev/name eventname + ::ev/origin "menu:in-app"})) + (dom/open-new-window url))))] + + [:> dropdown-menu* {:show true + :class (stl/css :sub-menu :about) + :on-close on-close} + + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :on-click show-release-notes} + (tr "labels.version-notes" (:base version))] + + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :data-url "https://github.com/penpot/penpot/blob/develop/CHANGES.md" + :on-click handle-click-url + :data-eventname "explore-changelog-click"} + (tr "labels.penpot-changelog")] + + [:> dropdown-menu-item* {:class (stl/css :submenu-item) + :data-url "https://penpot.app/terms" + :on-click handle-click-url + :data-eventname "explore-terms-service-click"} + (tr "auth.terms-of-service")]])) + (mf/defc profile-section* [{:keys [profile team]}] (let [show-profile-menu* (mf/use-state false) show-profile-menu? (deref show-profile-menu*) + sub-menu* (mf/use-state false) + sub-menu (deref sub-menu*) + version (:full cf/version) + + close-sub-menu + (mf/use-fn + (fn [event] + (dom/stop-propagation event) + (reset! sub-menu* nil))) photo (cf/resolve-profile-photo-url profile) @@ -779,15 +909,6 @@ (st/emit! (rt/nav section)) (st/emit! section)))) - show-release-notes - (mf/use-fn - (fn [event] - (let [version (:main cf/version)] - (st/emit! (ptk/event ::ev/event {::ev/name "show-release-notes" :version version})) - (if (and (kbd/alt? event) (kbd/mod? event)) - (st/emit! (modal/show {:type :onboarding})) - (st/emit! (modal/show {:type :release-notes :version version})))))) - show-comments* (mf/use-state false) show-comments? @show-comments* @@ -816,16 +937,6 @@ on-close (mf/use-fn #(reset! show-profile-menu* false)) - handle-click-url - (mf/use-fn - (fn [event] - (let [url (-> (dom/get-current-target event) - (dom/get-data "url"))] - (dom/open-new-window url)))) - - handle-feedback-click - (mf/use-fn #(on-click :settings-feedback %)) - handle-logout-click (mf/use-fn #(on-click (da/logout) %)) @@ -834,6 +945,15 @@ (mf/use-fn #(on-click :settings-profile %)) + on-menu-click + (mf/use-fn + (fn [event] + (dom/stop-propagation event) + (let [menu (-> (dom/get-current-target event) + (dom/get-data "testid") + (keyword))] + (reset! sub-menu* menu)))) + on-power-up-click (mf/use-fn (fn [] @@ -887,53 +1007,45 @@ [:li {:class (stl/css :profile-separator)}] - [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item) - :data-url "https://help.penpot.app" - :on-click handle-click-url - :data-testid "help-center-profile-opt"} - (tr "labels.help-center")] - [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item) - :data-url "https://community.penpot.app" - :on-click handle-click-url} - (tr "labels.community")] + [:> dropdown-menu-item* {:class (stl/css-case :profile-dropdown-item true) + :on-click on-menu-click + :on-key-down (fn [event] + (when (kbd/enter? event) + (on-menu-click event))) + :on-pointer-enter on-menu-click + :data-testid "help-learning" + :id "help-learning"} + [:span {:class (stl/css :item-name)} (tr "labels.help-learning")] + [:> icon* {:icon-id i/arrow :class (stl/css :open-arrow)}]] - [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item) - :data-url "https://www.youtube.com/c/Penpot" - :on-click handle-click-url} - (tr "labels.tutorials")] + [:> dropdown-menu-item* {:class (stl/css-case :profile-dropdown-item true) + :on-click on-menu-click + :on-key-down (fn [event] + (when (kbd/enter? event) + (on-menu-click event))) + :on-pointer-enter on-menu-click + :data-testid "community-contributions" + :id "community-contributions"} + [:span {:class (stl/css :item-name)} (tr "labels.community-contributions")] + [:> icon* {:icon-id i/arrow :class (stl/css :open-arrow)}]] - [:> dropdown-menu-item* {:tab-index "0" - :class (stl/css :profile-dropdown-item) - :on-click show-release-notes} - (tr "labels.release-notes")] + [:> dropdown-menu-item* {:class (stl/css-case :profile-dropdown-item true) + :on-click on-menu-click + :on-key-down (fn [event] + (when (kbd/enter? event) + (on-menu-click event))) + :on-pointer-enter on-menu-click + :data-testid "about-penpot" + :id "about-penpot"} + + [:div {:class (stl/css :about-penpot)} + [:span {:class (stl/css :item-name)} (tr "labels.about-penpot")] + [:span {:class (stl/css :menu-version) :title version} version]] + [:> icon* {:icon-id i/arrow :class (stl/css :open-arrow)}]] [:li {:class (stl/css :profile-separator)}] - [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item) - :data-url "https://penpot.app/libraries-templates" - :on-click handle-click-url - :data-testid "libraries-templates-profile-opt"} - (tr "labels.libraries-and-templates")] - - [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item) - :data-url "https://github.com/penpot/penpot" - :on-click handle-click-url} - (tr "labels.github-repo")] - - [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item) - :data-url "https://penpot.app/terms" - :on-click handle-click-url} - (tr "auth.terms-of-service")] - - [:li {:class (stl/css :profile-separator)}] - - (when (contains? cf/flags :user-feedback) - [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item) - :on-click handle-feedback-click - :data-testid "feedback-profile-opt"} - (tr "labels.give-feedback")]) - [:> dropdown-menu-item* {:class (stl/css :profile-dropdown-item :item-with-icon) :on-click handle-logout-click :data-testid "logout-profile-opt"} @@ -943,7 +1055,18 @@ (when (and team profile) [:> comments-icon* {:profile profile - :on-show-comments handle-show-comments}])]])) + :on-show-comments handle-show-comments}])] + + (case sub-menu + :help-learning + [:> help-learning-menu* {:on-close close-sub-menu :on-click on-click}] + + :community-contributions + [:> community-contributions-menu* {:on-close close-sub-menu}] + + :about-penpot + [:> about-penpot-menu* {:on-close close-sub-menu}] + nil)])) (mf/defc sidebar* {::mf/props :obj diff --git a/frontend/src/app/main/ui/dashboard/sidebar.scss b/frontend/src/app/main/ui/dashboard/sidebar.scss index a885a89f3e..9fb9235aa6 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.scss +++ b/frontend/src/app/main/ui/dashboard/sidebar.scss @@ -9,6 +9,8 @@ @use "../ds/spacing.scss" as *; @use "../ds/_borders.scss" as *; @use "../ds/_sizes.scss" as *; +@use "../ds/_utils.scss" as *; +@use "../ds/z-index.scss" as *; @use "common/refactor/common-dashboard"; @use "common/refactor/common-refactor.scss" as deprecated; @@ -24,7 +26,7 @@ margin: 0 var(--sp-l) 0 0; border-right: $b-1 solid var(--panel-border-color); background-color: var(--panel-background-color); - z-index: deprecated.$z-index-1; + z-index: var(--z-index-dropdown); } //SIDEBAR CONTENT COMPONENT @@ -392,17 +394,81 @@ .profile-dropdown { @extend .menu-dropdown; - left: var(--sp-l); - bottom: deprecated.$s-72; - min-width: $sz-252; + inset-inline-start: var(--sp-s); + inset-block-end: px2rem(72); // 72 is the height of the profile button + min-width: calc(100% - var(--sp-s)); // TODO ADD animation fadeInUp } .profile-dropdown-item { @extend .menu-item-base; - @include t.use-typography("title-small"); - height: $sz-40; - padding: var(--sp-s) var(--sp-l); + @include t.use-typography("body-medium"); + block-size: $sz-40; + margin-block-end: var(--sp-xs); + padding: var(--sp-s); + padding-inline-start: var(--sp-l); + + &:hover { + color: var(--menu-foreground-color-hover); + + .open-arrow { + --icon-stroke-color: var(--menu-foreground-color-hover); + } + } +} + +.profile-dropdown-item .open-arrow { + @include deprecated.flexCenter; +} + +.profile-dropdown-item .open-arrow svg { + @extend .button-icon; + stroke: var(--icon-foreground); +} + +.sub-menu { + @extend .menu-dropdown; + inset-inline-start: calc(deprecated.$s-292 + var(--sp-s)); + min-width: deprecated.$s-192; +} + +.sub-menu.help-learning { + inset-block-end: deprecated.$s-72; +} + +.sub-menu.community { + inset-block-end: deprecated.$s-120; +} + +.sub-menu.about { + inset-block-end: $sz-24; +} + +.submenu-item { + @extend .menu-item-base; + @include t.use-typography("body-medium"); + block-size: $sz-40; + margin-block-end: var(--sp-xs); + padding-block: var(--sp-s); + padding-inline: var(--sp-l); + + &:hover { + color: var(--menu-foreground-color-hover); + } +} + +.about-penpot { + align-items: baseline; + display: grid; + grid-template-columns: auto 1fr; +} + +.menu-version { + @include t.use-typography("code-font"); + @include deprecated.textEllipsis; + color: var(--color-foreground-secondary); + margin-inline-start: var(--sp-s); + text-transform: uppercase; } .profile-separator { diff --git a/frontend/src/app/main/ui/workspace/main_menu.cljs b/frontend/src/app/main/ui/workspace/main_menu.cljs index e8adccd671..e71929e508 100644 --- a/frontend/src/app/main/ui/workspace/main_menu.cljs +++ b/frontend/src/app/main/ui/workspace/main_menu.cljs @@ -53,22 +53,46 @@ ::mf/wrap [mf/memo]} [{:keys [layout on-close]}] (let [nav-to-helpc-center - (mf/use-fn #(dom/open-new-window "https://help.penpot.app")) + (mf/use-fn + (fn [] + (st/emit! (ptk/event ::ev/event {::ev/name "explore-help-center-click" + ::ev/origin "workspace-menu:in-app"})) + (dom/open-new-window "https://help.penpot.app"))) nav-to-community - (mf/use-fn #(dom/open-new-window "https://community.penpot.app")) + (mf/use-fn + (fn [] + (st/emit! (ptk/event ::ev/event {::ev/name "explore-community-click" + ::ev/origin "workspace-menu:in-app"})) + (dom/open-new-window "https://community.penpot.app"))) nav-to-youtube - (mf/use-fn #(dom/open-new-window "https://www.youtube.com/c/Penpot")) + (mf/use-fn + (fn [] + (st/emit! (ptk/event ::ev/event {::ev/name "explore-tutorials-click" + ::ev/origin "workspace-menu:in-app"})) + (dom/open-new-window "https://www.youtube.com/c/Penpot"))) nav-to-templates - (mf/use-fn #(dom/open-new-window "https://penpot.app/libraries-templates")) + (mf/use-fn + (fn [] + (st/emit! (ptk/event ::ev/event {::ev/name "explore-libraries-click" + ::ev/origin "workspace"})) + (dom/open-new-window "https://penpot.app/libraries-templates"))) nav-to-github - (mf/use-fn #(dom/open-new-window "https://github.com/penpot/penpot")) + (mf/use-fn + (fn [] + (st/emit! (ptk/event ::ev/event {::ev/name "explore-github-repository-click" + ::ev/origin "workspace-menu:in-app"})) + (dom/open-new-window "https://github.com/penpot/penpot"))) nav-to-terms - (mf/use-fn #(dom/open-new-window "https://penpot.app/terms")) + (mf/use-fn + (fn [] + (st/emit! (ptk/event ::ev/event {::ev/name "explore-terms-service-click" + ::ev/origin "workspace-menu:in-app"})) + (dom/open-new-window "https://penpot.app/terms"))) nav-to-feedback (mf/use-fn #(st/emit! (dcm/go-to-feedback))) @@ -92,6 +116,7 @@ (fn [event] (let [version (:main cf/version)] (st/emit! (ptk/event ::ev/event {::ev/name "show-release-notes" :version version})) + (println version) (if (and (kbd/alt? event) (kbd/mod? event)) (st/emit! (modal/show {:type :onboarding})) (st/emit! (modal/show {:type :release-notes :version version}))))))] diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 4adfab8ae6..cd4e5c1af0 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2172,11 +2172,26 @@ msgstr "Go back" msgid "labels.graphic-design" msgstr "Graphic design" -#: src/app/main/ui/dashboard/sidebar.cljs:894, src/app/main/ui/workspace/main_menu.cljs:108, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1084, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1109, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1295 +msgid "labels.help-learning" +msgstr "Help & Learning" + +msgid "labels.community-contributions" +msgstr "Community & Contributions" + +msgid "labels.about-penpot" +msgstr "About Penpot" + +#: src/app/main/ui/dashboard/sidebar.cljs:1039, src/app/main/ui/workspace/main_menu.cljs:107, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1084, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1109, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1295 msgid "labels.help-center" msgstr "Help Center" -#: src/app/main/ui/dashboard/templates.cljs:91 +msgid "labels.learning-center" +msgstr "Learning Center" + +msgid "labels.penpot-hub" +msgstr "Penpot hub" + +#: src/app/main/ui/dashboard/templates.cljs:90 msgid "labels.hide" msgstr "Hide" @@ -2394,6 +2409,12 @@ msgstr "Refresh" msgid "labels.release-notes" msgstr "Release notes" +msgid "labels.version-notes" +msgstr "Version %s notes" + +msgid "labels.penpot-changelog" +msgstr "Penpot Changelog" + #: src/app/main/ui/workspace.cljs #, unused msgid "labels.reload-file" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 6f9da5ad86..61e3e39090 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -2178,11 +2178,26 @@ msgstr "Volver" msgid "labels.graphic-design" msgstr "Diseño gráfico" -#: src/app/main/ui/dashboard/sidebar.cljs:894, src/app/main/ui/workspace/main_menu.cljs:108, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1084, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1109, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1295 +msgid "labels.help-learning" +msgstr "Ayuda y aprendizaje" + +msgid "labels.community-contributions" +msgstr "Comunidad y contribuciones" + +msgid "labels.about-penpot" +msgstr "Acerca de Penpot" + +#: src/app/main/ui/dashboard/sidebar.cljs:1039, src/app/main/ui/workspace/main_menu.cljs:107, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1084, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1109, src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs:1295 msgid "labels.help-center" msgstr "Centro de ayuda" -#: src/app/main/ui/dashboard/templates.cljs:91 +msgid "labels.learning-center" +msgstr "Centro de aprendizaje" + +msgid "labels.penpot-hub" +msgstr "Penpot hub" + +#: src/app/main/ui/dashboard/templates.cljs:90 msgid "labels.hide" msgstr "Ocultar" @@ -2392,6 +2407,12 @@ msgstr "Referencia" msgid "labels.release-notes" msgstr "Notas de versión" +msgid "labels.version-notes" +msgstr "Notas versión %s" + +msgid "labels.penpot-changelog" +msgstr "Registro de cambios de Penpot" + #: src/app/main/ui/workspace.cljs #, unused msgid "labels.reload-file"