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

Feat hide clear button #174

Merged
merged 15 commits into from
Sep 10, 2024
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
5 changes: 5 additions & 0 deletions .changeset/nice-adults-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openproject/primer-view-components': minor
---

Toggle the visibility of the clear button on the SubHeader component's filter input based on its content.
4 changes: 4 additions & 0 deletions app/components/primer/open_project/sub_header.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@
width: 100%;
gap: 8px;
}

.SubHeader-filterInput_hiddenClearButton + button.FormControl-input-trailingAction {
display: none;
}
20 changes: 18 additions & 2 deletions app/components/primer/open_project/sub_header.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class SubHeader < Primer::Component
renders_one :filter_input, lambda { |name:, label:, **system_arguments|
system_arguments[:classes] = class_names(
system_arguments[:classes],
"SubHeader-filterInput"
"SubHeader-filterInput",
"SubHeader-filterInput_hiddenClearButton"
)
system_arguments[:placeholder] ||= I18n.t("button_filter")
system_arguments[:leading_visual] ||= { icon: :search }
Expand All @@ -47,10 +48,25 @@ class SubHeader < Primer::Component
system_arguments[:data] ||= {}
system_arguments[:data][:target]= "sub-header.filterInput"

system_arguments[:show_clear_button] = true if system_arguments[:show_clear_button].nil?

if system_arguments[:show_clear_button]
system_arguments[:data] = merge_data(
system_arguments,
{
data: {
action: <<~JS
input:sub-header#toggleFilterInputClearButton
focus:sub-header#toggleFilterInputClearButton
JS
}
}
)
end

@mobile_filter_trigger = Primer::Beta::IconButton.new(icon: system_arguments[:leading_visual][:icon],
display: [:inline_flex, :none],
aria: {label: label },
aria: { label: label },
"data-action": "click:sub-header#expandFilterInput",
"data-targets": HIDDEN_FILTER_TARGET_SELECTOR)

Expand Down
40 changes: 39 additions & 1 deletion app/components/primer/open_project/sub_header_element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,31 @@ import {controller, target, targets} from '@github/catalyst'

@controller
class SubHeaderElement extends HTMLElement {
@target filterInput: HTMLElement
@target filterInput: HTMLInputElement
@targets hiddenItemsOnExpandedFilter: HTMLElement[]
@targets shownItemsOnExpandedFilter: HTMLElement[]

connectedCallback() {
this.setupFilterInputClearButton()
}

setupFilterInputClearButton() {
this.waitForCondition(
() => Boolean(this.filterInput),
() => {
this.toggleFilterInputClearButton()
},
)
}

toggleFilterInputClearButton() {
if (this.filterInput.value.length > 0) {
this.filterInput.classList.remove('SubHeader-filterInput_hiddenClearButton')
} else {
this.filterInput.classList.add('SubHeader-filterInput_hiddenClearButton')
}
}

expandFilterInput() {
for (const item of this.hiddenItemsOnExpandedFilter) {
item.classList.add('d-none')
Expand All @@ -31,6 +52,23 @@ class SubHeaderElement extends HTMLElement {

this.classList.remove('SubHeader--expandedSearch')
}

// Waits for condition to return true. If it returns false initially, this function creates a
// MutationObserver that calls body() whenever the contents of the component change.
private waitForCondition(condition: () => boolean, body: () => void) {
if (condition()) {
body()
} else {
const mutationObserver = new MutationObserver(() => {
if (condition()) {
body()
mutationObserver.disconnect()
}
})

mutationObserver.observe(this, {childList: true, subtree: true})
}
}
}

declare global {
Expand Down
17 changes: 15 additions & 2 deletions previews/primer/open_project/sub_header_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,23 @@ class SubHeaderPreview < ViewComponent::Preview
# @param show_filter_input toggle
# @param show_filter_button toggle
# @param show_action_button toggle
# @param show_clear_button toggle
# @param text text
def playground(show_filter_input: true, show_filter_button: true, show_action_button: true, text: nil)
# @param value text
def playground(
show_filter_input: true,
show_clear_button: true,
show_filter_button: true,
show_action_button: true,
text: nil,
value: nil
)
render(Primer::OpenProject::SubHeader.new) do |component|
component.with_filter_input(name: "filter", label: "Filter") if show_filter_input
component.with_filter_input(
name: "filter",
label: "Filter",
show_clear_button: show_clear_button,
value: value) if show_filter_input
component.with_filter_button do |button|
button.with_trailing_visual_counter(count: "15")
"Filter"
Expand Down
3 changes: 3 additions & 0 deletions static/classes.json
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,9 @@
"SubHeader-filterContainer": [
"Primer::OpenProject::SubHeader"
],
"SubHeader-filterInput_hiddenClearButton": [
"Primer::OpenProject::SubHeader"
],
"SubHeader-leftPane": [
"Primer::OpenProject::SubHeader"
],
Expand Down
39 changes: 39 additions & 0 deletions test/components/primer/open_project/sub_header_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,43 @@ def test_renders_a_custom_filter_button
assert_selector(".SubHeader .MyCustomButton")
assert_selector(".SubHeader .SubHeader-bottomPane .ABottomPane")
end

def test_renders_a_clear_button_when_show_clear_button_is_set
render_inline(Primer::OpenProject::SubHeader.new) do |component|
component.with_filter_input(
name: "filter",
label: "Filter",
show_clear_button: true,
value: "value is set"
)
end

assert_selector(".SubHeader")
assert_selector(
".SubHeader-filterInput"\
"[data-action=\"input:sub-header#toggleFilterInputClearButton\n"\
"focus:sub-header#toggleFilterInputClearButton\n\"]"
)
assert_selector(".FormControl-input-trailingAction[data-action=\"click:primer-text-field#clearContents\"]")
end

def test_does_not_render_input_events_when_show_clear_button_is_not_set
render_inline(Primer::OpenProject::SubHeader.new) do |component|
component.with_filter_input(
name: "filter",
label: "Filter",
show_clear_button: false,
value: "value is set"
)
end

assert_selector(".SubHeader")
assert_selector(".SubHeader-filterInput")
assert_no_selector(
".SubHeader-filterInput"\
"[data-action=\"input:sub-header#toggleFilterInputClearButton\n"\
"focus:sub-header#toggleFilterInputClearButton\n\"]"
)
assert_no_selector(".FormControl-input-trailingAction[data-action=\"click:primer-text-field#clearContents\"]")
end
end
1 change: 1 addition & 0 deletions test/css/component_specific_selectors_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ class ComponentSpecificSelectorsTest < Minitest::Test
],
Primer::OpenProject::SubHeader => [
".SubHeader--expandedSearch",
".SubHeader-filterInput_hiddenClearButton"
]
}.freeze

Expand Down
27 changes: 27 additions & 0 deletions test/system/open_project/sub_header_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,31 @@ def test_renders_component

assert_selector(".SubHeader")
end

def render_clear_button_with_an_initial_value
visit_preview(:playground, show_clear_button: true, value: "value")

assert_selector(".FormControl-input-wrap--trailingAction")
assert_selector("button.FormControl-input-trailingAction")
end

def test_clear_button_functionality
visit_preview(:playground, show_clear_button: true)
# no clear button with empty value
assert_no_selector("button.FormControl-input-trailingAction")

fill_in "filter", with: "value"

# Clear the field with the "x" button
find(".FormControl-input-trailingAction").click
assert_no_selector("button.FormControl-input-trailingAction")

fill_in "filter", with: "value"
assert_selector("button.FormControl-input-trailingAction")

# Clear the field with backspace
filter_field = find_field "filter"
"value".length.times { filter_field.send_keys [:backspace] }
assert_no_selector("button.FormControl-input-trailingAction")
end
end
Loading