From 91f5a57e1b2df25844b96971b08d2a5a8370255f Mon Sep 17 00:00:00 2001 From: Klaus Zanders Date: Fri, 26 Jul 2024 18:43:11 +0200 Subject: [PATCH] Add `turbo: true` as an option for ToggleSwitch (#2964) --- .changeset/hip-glasses-wave.md | 6 +++++ app/components/primer/alpha/toggle_switch.rb | 6 +++-- app/components/primer/alpha/toggle_switch.ts | 23 +++++++++++++++---- .../primer/alpha/toggle_switch_preview.rb | 4 ++++ test/components/alpha/toggle_switch_test.rb | 6 +++++ test/system/alpha/toggle_switch_test.rb | 14 +++++++++++ 6 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 .changeset/hip-glasses-wave.md diff --git a/.changeset/hip-glasses-wave.md b/.changeset/hip-glasses-wave.md new file mode 100644 index 0000000000..acff89455b --- /dev/null +++ b/.changeset/hip-glasses-wave.md @@ -0,0 +1,6 @@ +--- +"@primer/view-components": minor +--- + +Add `turbo: true` as an parameter for the `ToggleSwitch` component and treat the respoonse (when it has the correct MIME type) as a [Turbo Stream](https://turbo.hotwired.dev/handbook/streams). The new `turbo` paramater defaults to false +and with that the HTTP response is simply ignored as is the current behavior. diff --git a/app/components/primer/alpha/toggle_switch.rb b/app/components/primer/alpha/toggle_switch.rb index 79b740e084..ee992a2f5b 100644 --- a/app/components/primer/alpha/toggle_switch.rb +++ b/app/components/primer/alpha/toggle_switch.rb @@ -26,12 +26,14 @@ class ToggleSwitch < Primer::Component # @param enabled [Boolean] Whether or not the toggle switch responds to user input. # @param size [Symbol] What size toggle switch to render. <%= one_of(Primer::Alpha::ToggleSwitch::SIZE_OPTIONS) %> # @param status_label_position [Symbol] Which side of the toggle switch to render the status label. <%= one_of(Primer::Alpha::ToggleSwitch::STATUS_LABEL_POSITION_OPTIONS) %> + # @param turbo [Boolean] Whether or not to request a turbo stream and render the response as such. # @param system_arguments [Hash] <%= link_to_system_arguments_docs %> - def initialize(src: nil, csrf_token: nil, checked: false, enabled: true, size: SIZE_DEFAULT, status_label_position: STATUS_LABEL_POSITION_DEFAULT, **system_arguments) + def initialize(src: nil, csrf_token: nil, checked: false, enabled: true, size: SIZE_DEFAULT, status_label_position: STATUS_LABEL_POSITION_DEFAULT, turbo: false, **system_arguments) @src = src @csrf_token = csrf_token @checked = checked @enabled = enabled + @turbo = turbo @system_arguments = system_arguments @size = fetch_or_fallback(SIZE_OPTIONS, size, SIZE_DEFAULT) @@ -82,7 +84,7 @@ def before_render @system_arguments[:data] = merge_data( @system_arguments, - { data: { csrf: @csrf_token } } + { data: { csrf: @csrf_token, turbo: @turbo } } ) end end diff --git a/app/components/primer/alpha/toggle_switch.ts b/app/components/primer/alpha/toggle_switch.ts index f5bc66212d..148e23ca33 100644 --- a/app/components/primer/alpha/toggle_switch.ts +++ b/app/components/primer/alpha/toggle_switch.ts @@ -1,10 +1,11 @@ -import {controller, target} from '@github/catalyst' +import {controller, target, attr} from '@github/catalyst' @controller class ToggleSwitchElement extends HTMLElement { @target switch: HTMLElement @target loadingSpinner: HTMLElement @target errorIcon: HTMLElement + @attr turbo = false private toggling = false @@ -158,13 +159,19 @@ class ToggleSwitchElement extends HTMLElement { let response + const requestHeaders: {[key: string]: string} = { + 'Requested-With': 'XMLHttpRequest', + } + + if (this.turbo) { + requestHeaders['Accept'] = 'text/vnd.turbo-stream.html' + } + try { response = await fetch(this.src, { credentials: 'same-origin', method: 'POST', - headers: { - 'Requested-With': 'XMLHttpRequest', - }, + headers: requestHeaders, body, }) } catch (error) { @@ -174,12 +181,20 @@ class ToggleSwitchElement extends HTMLElement { if (!response.ok) { throw new Error(await response.text()) } + + const contentType = response.headers.get('Content-Type') + if (window.Turbo && this.turbo && contentType?.startsWith('text/vnd.turbo-stream.html')) { + window.Turbo.renderStreamMessage(await response.text()) + } } } declare global { interface Window { ToggleSwitchElement: typeof ToggleSwitchElement + Turbo: { + renderStreamMessage: (message: string) => void + } } } diff --git a/previews/primer/alpha/toggle_switch_preview.rb b/previews/primer/alpha/toggle_switch_preview.rb index 535da6c53a..a9835ef844 100644 --- a/previews/primer/alpha/toggle_switch_preview.rb +++ b/previews/primer/alpha/toggle_switch_preview.rb @@ -58,6 +58,10 @@ def with_csrf_token def with_bad_csrf_token render(Primer::Alpha::ToggleSwitch.new(src: UrlHelpers.toggle_switch_index_path, csrf_token: "i_am_a_criminal")) end + + def with_turbo + render(Primer::Alpha::ToggleSwitch.new(src: UrlHelpers.toggle_switch_index_path, turbo: true)) + end end end end diff --git a/test/components/alpha/toggle_switch_test.rb b/test/components/alpha/toggle_switch_test.rb index 30a2d9fe1d..8b753b3d31 100644 --- a/test/components/alpha/toggle_switch_test.rb +++ b/test/components/alpha/toggle_switch_test.rb @@ -68,6 +68,12 @@ def test_csrf_token assert_selector("[data-csrf]") end + + def test_turbo + render_inline(Primer::Alpha::ToggleSwitch.new(src: "/foo", turbo: true)) + + assert_selector("[data-turbo]") + end end end end diff --git a/test/system/alpha/toggle_switch_test.rb b/test/system/alpha/toggle_switch_test.rb index 8458adffbb..56d028bbca 100644 --- a/test/system/alpha/toggle_switch_test.rb +++ b/test/system/alpha/toggle_switch_test.rb @@ -88,6 +88,20 @@ def test_fetch_made_with_correct_headers assert_equal "XMLHttpRequest", ToggleSwitchController.last_request.headers["HTTP_REQUESTED_WITH"] end + def test_fetch_made_with_turbo + visit_preview(:with_turbo) + + refute_selector(".ToggleSwitch--checked") + find("toggle-switch").click + assert_selector(".ToggleSwitch--checked") + + wait_for_request + + assert_equal "text/vnd.turbo-stream.html", ToggleSwitchController.last_request.headers["HTTP_ACCEPT"] + end + + + private def wait_for_spinner