diff --git a/.changeset/wild-avocados-smash.md b/.changeset/wild-avocados-smash.md new file mode 100644 index 0000000000..df5e1e97d1 --- /dev/null +++ b/.changeset/wild-avocados-smash.md @@ -0,0 +1,5 @@ +--- +"@openproject/primer-view-components": minor +--- + +Add `Primer::OpenProject::ZenModeButton` component. By clicking on the button, screen will be in full-mode or back in normal-mode. diff --git a/app/components/primer/open_project/zen_mode_button.html.erb b/app/components/primer/open_project/zen_mode_button.html.erb new file mode 100644 index 0000000000..4e6f5b2874 --- /dev/null +++ b/app/components/primer/open_project/zen_mode_button.html.erb @@ -0,0 +1,11 @@ +<%= render(Primer::BaseComponent.new(**@system_arguments)) do %> + <%= render( + Primer::Beta::IconButton.new( + scheme: :default, + id: "zenModeButton", + icon: "screen-normal", + aria: { label: I18n.t("zen_mode") }, + data: { target: "zen-mode-button.button", action: "click:zen-mode-button#performAction" } + ) + ) %> +<% end %> diff --git a/app/components/primer/open_project/zen_mode_button.rb b/app/components/primer/open_project/zen_mode_button.rb new file mode 100644 index 0000000000..812cacc862 --- /dev/null +++ b/app/components/primer/open_project/zen_mode_button.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Primer + module OpenProject + # Add a general description of component here + # Add additional usage considerations or best practices that may aid the user to use the component correctly. + # @accessibility Add any accessibility considerations + class ZenModeButton < Primer::Component + status :open_project + + # @param system_arguments [Hash] <%= link_to_system_arguments_docs %> + def initialize(**system_arguments) + @system_arguments = system_arguments + @system_arguments[:tag] = "zen-mode-button" + @system_arguments[:classes] = + class_names( + @system_arguments[:classes], + "ZenModeButton" + ) + end + end + end +end diff --git a/app/components/primer/open_project/zen_mode_button.ts b/app/components/primer/open_project/zen_mode_button.ts new file mode 100644 index 0000000000..89f8447893 --- /dev/null +++ b/app/components/primer/open_project/zen_mode_button.ts @@ -0,0 +1,42 @@ +import {controller, target} from '@github/catalyst' + +@controller +class ZenModeButtonElement extends HTMLElement { + @target button: HTMLElement + inZenMode = false + + private deactivateZenMode() { + this.inZenMode = false + this.button.setAttribute('aria-pressed', 'false') + if (document.exitFullscreen) { + void document.exitFullscreen() + } + } + + private activateZenMode() { + this.inZenMode = true + this.button.setAttribute('aria-pressed', 'true') + if (document.documentElement.requestFullscreen) { + void document.documentElement.requestFullscreen() + } + } + + public performAction() { + if (this.inZenMode) { + this.deactivateZenMode() + } else { + this.activateZenMode() + } + } +} + +declare global { + interface Window { + ZenModeButtonElement: typeof ZenModeButtonElement + } +} + +if (!window.customElements.get('zen-mode-button')) { + window.ZenModeButtonElement = ZenModeButtonElement + window.customElements.define('zen-mode-button', ZenModeButtonElement) +} diff --git a/app/components/primer/primer.ts b/app/components/primer/primer.ts index 817a2dd9ea..2158437b3e 100644 --- a/app/components/primer/primer.ts +++ b/app/components/primer/primer.ts @@ -23,3 +23,4 @@ import '../../../lib/primer/forms/primer_text_field' import '../../../lib/primer/forms/toggle_switch_input' import './alpha/action_menu/action_menu_element' import './open_project/page_header_element' +import './open_project/zen_mode_button' diff --git a/previews/primer/open_project/zen_mode_button_preview.rb b/previews/primer/open_project/zen_mode_button_preview.rb new file mode 100644 index 0000000000..28ad21d622 --- /dev/null +++ b/previews/primer/open_project/zen_mode_button_preview.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +# Setup Playground to use all available component props +# Setup Features to use individual component props and combinations + +module Primer + module OpenProject + # @label ZenModeButton + class ZenModeButtonPreview < ViewComponent::Preview + # @label Default + # @snapshot + def default + render(Primer::OpenProject::ZenModeButton.new) + end + + # @label Playground + def playground + render(Primer::OpenProject::ZenModeButton.new) + end + end + end +end diff --git a/test/components/component_test.rb b/test/components/component_test.rb index f834b6099e..0df6074f4e 100644 --- a/test/components/component_test.rb +++ b/test/components/component_test.rb @@ -8,6 +8,7 @@ class PrimerComponentTest < Minitest::Test # Components with any arguments necessary to make them render COMPONENTS_WITH_ARGS = [ + [Primer::OpenProject::ZenModeButton, {}], [Primer::OpenProject::InputGroup, {}, proc { |component| component.with_text_input(name: "a name", label: "My input group", value: "Copyable value") component.with_trailing_action_clipboard_copy_button(id: "button", value: "Copyable value", aria: { label: "Copy some text" }) diff --git a/test/components/primer/open_project/zen_mode_button_test.rb b/test/components/primer/open_project/zen_mode_button_test.rb new file mode 100644 index 0000000000..3049a8b890 --- /dev/null +++ b/test/components/primer/open_project/zen_mode_button_test.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require "components/test_helper" + +class PrimerOpenProjectZenModeButtonTest < Minitest::Test + include Primer::ComponentTestHelpers + + def test_renders + render_inline(Primer::OpenProject::ZenModeButton.new) + + assert_selector(".ZenModeButton .octicon-screen-normal") + end +end diff --git a/test/system/open_project/zen_mode_button_test.rb b/test/system/open_project/zen_mode_button_test.rb new file mode 100644 index 0000000000..25507dedee --- /dev/null +++ b/test/system/open_project/zen_mode_button_test.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require "system/test_case" + +class IntegrationOpenProjectZenModeButtonTest < System::TestCase + def test_renders_component + visit_preview(:default) + + assert_selector(".ZenModeButton") + end +end