Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
4477b8c
[#75274] Unify truncation controllers
myabc May 22, 2026
17dec9f
[#75274] Compose from AttributeComponent
myabc May 22, 2026
da299b9
[#75274] Add and document ExpandableText preview
myabc May 22, 2026
7f03c9c
[#75274] Extract VerticalTruncateComponent
myabc May 30, 2026
b08309d
Privatise expandable-text controller; rename API
myabc May 30, 2026
78e0b9e
Deep-dup expander_arguments to protect caller hashes
myabc May 30, 2026
ba70cd6
Add VerticalTruncateComponent Lookbook preview
myabc May 30, 2026
d06192e
Merge branch 'dev' into code-maintenance/75274-document-expandable-text
myabc Jun 2, 2026
8a016f5
Own the dialog slot in ExpandableText
myabc Jun 16, 2026
ec5558b
[DREAM-693] Rework the ExpandableText docs
myabc Jun 16, 2026
0f8f839
Merge branch 'dev' into code-maintenance/75274-document-expandable-text
myabc Jun 16, 2026
6a89622
Expose dialog ARIA on the ExpandableText expander
myabc Jun 16, 2026
7034c07
Disable preview cop for VerticalTruncateComponent
myabc Jun 16, 2026
1a92b29
Use accessible selectors in ExpandableText spec
myabc Jun 16, 2026
0915bc7
Increase height of vertical preview
myabc Jun 17, 2026
09e58d3
Extract ExpandableText lines default to a constant
myabc Jun 17, 2026
2bd9d11
Fix broken Lookbook preview link in ExpandableText docs
myabc Jun 17, 2026
9b47949
Rename ExpandableText direction to truncate
myabc Jun 17, 2026
a957c67
Bump ExpandableText line clamp limit to 8
myabc Jun 17, 2026
852b075
Floor multi_line truncation at 2 lines
myabc Jun 17, 2026
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
1 change: 0 additions & 1 deletion app/components/_index.sass
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
@import "op_primer/full_page_prompt_component"
@import "op_primer/form_helpers"
@import "op_primer/inline_macro_component"
@import "open_project/common/attribute_component"
@import "open_project/common/attribute_help_text_component"
@import "open_project/common/attribute_help_text_caption_component"
@import "open_project/common/attribute_label_component"
Expand Down
28 changes: 17 additions & 11 deletions app/components/op_primer/expandable_text_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
<%= render(Primer::BaseComponent.new(**@system_arguments)) do %>
<%= render Primer::Beta::Truncate.new(
data: { truncation_target: "truncate" },
flex: 1
) do %>
<%= content %>
<% if truncation.vertical? %>
<%= render Primer::BaseComponent.new(
tag: :div,
flex: 1,
classes: "line-clamp-#{lines} min-width-0",
data: { truncation_target: "truncate" }
) do %>
<%= content %>
<% end %>
<% else %>
<%= render Primer::Beta::Truncate.new(
flex: 1,
data: { truncation_target: "truncate" }
) do %>
<%= content %>
<% end %>
<% end %>

<%= render Primer::Alpha::HiddenTextExpander.new(
hidden: true,
mt: 1,
aria: { label: t(:"js.label_expand_text") },
data: { truncation_target: "expander" }
) %>
<%= render Primer::Alpha::HiddenTextExpander.new(**expander_arguments) %>
<% end %>
73 changes: 69 additions & 4 deletions app/components/op_primer/expandable_text_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,87 @@
#++
#
module OpPrimer
class ExpandableTextComponent < Primer::Component # rubocop:disable OpenProject/AddPreviewForViewComponent
def initialize(**system_arguments)
# Truncates block content and exposes an expander to reveal the full text.
#
# Two truncation modes are supported:
#
# - `:horizontal` clips a single line with `Primer::Beta::Truncate`.
# - `:vertical` clamps to `lines` rows via the CSS `line-clamp` utility.
#
# The companion `truncation` Stimulus controller toggles the expanded state.
# With `inline: true` the expander reveals the text in place; with
# `inline: false` the expander is left to a caller-provided action (for
# example, a dialog) and only its visibility is managed.
class ExpandableTextComponent < Primer::Component
TRUNCATION_OPTIONS = %i[horizontal vertical].freeze
TRUNCATION_DEFAULT = :horizontal

# Supported `.line-clamp-N` utility classes (see `_text_utils.sass`).
LINE_CLAMP_RANGE = (1..6)

attr_reader :truncation, :lines, :inline, :expander_arguments

# @param truncation [Symbol] truncation direction. `:horizontal` clips a
# single line; `:vertical` clamps to `lines` rows.
# @param lines [Integer] number of visible rows in `:vertical` mode, clamped to `1..6`.
# @param inline [Boolean] whether the expander reveals the text in place.
# When `false`, the expander's click is left to the caller (e.g. a dialog)
# and only its visibility is managed.
# @param expander_arguments [Hash] system arguments forwarded to the
# `Primer::Alpha::HiddenTextExpander`.
# @param system_arguments [Hash] forwarded to the wrapping
# `Primer::BaseComponent`.
# rubocop:disable Metrics/AbcSize
def initialize(
truncation: TRUNCATION_DEFAULT,
lines: 3,
Comment thread
myabc marked this conversation as resolved.
Outdated
inline: true,
expander_arguments: {},
**system_arguments
)
super()

@truncation = ActiveSupport::StringInquirer.new(
fetch_or_fallback(TRUNCATION_OPTIONS, truncation, TRUNCATION_DEFAULT).to_s
)
@lines = lines.to_i.clamp(LINE_CLAMP_RANGE)
@inline = inline

@system_arguments = deny_tag_argument(**system_arguments)
@system_arguments[:tag] = :div
@system_arguments[:display] = :flex
@system_arguments[:align_items] = :baseline
@system_arguments[:align_items] = @truncation.vertical? ? :flex_end : :baseline
@system_arguments[:data] = merge_data(
@system_arguments,
data: { controller: "truncation" }
data: {
controller: "truncation",
truncation_mode_value: @truncation,
truncation_inline_value: inline
}
)
@system_arguments[:classes] = class_names(
@system_arguments[:classes],
"gap-1 min-width-0"
)

set_expander_arguments!(expander_arguments)
end
# rubocop:enable Metrics/AbcSize

private

def set_expander_arguments!(expander_arguments)
@expander_arguments = expander_arguments.dup
@expander_arguments[:hidden] = true unless @expander_arguments.key?(:hidden)
Comment thread
myabc marked this conversation as resolved.
@expander_arguments[:mt] ||= 1
@expander_arguments[:aria] = merge_aria(
{ aria: { label: I18n.t("js.label_expand_text") } },
@expander_arguments
)
@expander_arguments[:data] = merge_data(
{ data: { truncation_target: "expander" } },
@expander_arguments
)
Comment thread
myabc marked this conversation as resolved.
end
end
end
82 changes: 29 additions & 53 deletions app/components/open_project/common/attribute_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,54 +1,30 @@
<div
data-controller="attribute"
data-attribute-background-reference-id-value="<%= background_reference_id %>"
class="op-long-text-attribute">
<%= render(
OpPrimer::ExpandableTextComponent.new(
truncation: truncation_direction,
lines:,
inline: false,
expander_arguments: {
hidden: !show_expander?,
aria: { label: I18n.t("label_attribute_expand_text", attribute: name) },
button_arguments: { data: { show_dialog_id: id } },
test_selector: "expand-button"
}
)
) do
render(
Primer::Beta::Text.new(classes: PARAGRAPH_CSS_CLASS, color: text_color)
.with_content(short_text)
)
end %>

<%= render(
Primer::Beta::Text.new(
tag: :div,
classes: ["op-long-text-attribute--text", PARAGRAPH_CSS_CLASS],
color: text_color,
style: "max-height: #{max_height};",
data: {
"attribute-target": "descriptionText"
}
)
) { short_text } %>

<%= render(
Primer::Beta::Text.new(
tag: :div,
display: display_expand_button_value,
classes: "op-long-text-attribute--text-hider",
data: { "attribute-target": "textHider" }
)
) %>

<%= render(
Primer::Alpha::HiddenTextExpander.new(
inline: false,
"aria-label": I18n.t("label_attribute_expand_text", attribute: name),
display: display_expand_button_value,
data: {
"attribute-target": "expandButton",
"test-selector": "expand-button"
},
button_arguments: { "data-show-dialog-id": id },
classes: "op-long-text-attribute--text-expander"
)
) %>

<%= render(
Primer::Alpha::Dialog.new(
id: id,
data: {
"test-selector": "attribute-dialog"
},
title: name,
size: :large
)
) do |component|
component.with_body { full_text }
component.with_header(variant: :large)
end %>
</div>
<%= render(
Primer::Alpha::Dialog.new(
id: id,
title: name,
size: :large,
test_selector: "attribute-dialog"
)
) do |component|
component.with_body_content(full_text)
component.with_header(variant: :large)
end %>
Comment thread
myabc marked this conversation as resolved.
Outdated
18 changes: 9 additions & 9 deletions app/components/open_project/common/attribute_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,26 @@ class AttributeComponent < Primer::Component
:name,
:description,
:lines,
:background_reference_id,
:format

PARAGRAPH_CSS_CLASS = "op-uc-p"

def initialize(id, name, description, lines: 1, background_reference_id: "content", format: true, **args)
def initialize(id, name, description, lines: 1, format: true, **args)
super()
@id = id
@name = name
@description = description
@system_arguments = args
@lines = lines
@background_reference_id = background_reference_id
@format = format
end

# `lines` only constrains height in vertical mode; a single line is best
# served by horizontal (single-line ellipsis) truncation.
def truncation_direction
lines > 1 ? :vertical : :horizontal
end

def short_text
if multi_type?
I18n.t(:label_preview_not_available)
Expand All @@ -64,18 +68,14 @@ def full_text
@full_text ||= format ? helpers.format_text(description) : description
end

def display_expand_button_value
multi_type? || body_children.length > 1 ? :block : :none
def show_expander?
multi_type? || body_children.length > 1
end

def text_color
:muted if multi_type?
end

def max_height
"#{lines * 1.6}em"
end

private

# rubocop:disable Rails/OutputSafety
Expand Down
17 changes: 0 additions & 17 deletions app/components/open_project/common/attribute_component.sass

This file was deleted.

6 changes: 6 additions & 0 deletions frontend/src/global_styles/content/_text_utils.sass
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
@for $i from 1 through 6
.line-clamp-#{$i}
@include line-clamp($i)

.expandable-text--expanded
// display: block makes -webkit-box-orient inert, so no need to reset it here
-webkit-line-clamp: unset !important
display: block !important
overflow: visible !important
84 changes: 0 additions & 84 deletions frontend/src/stimulus/controllers/dynamic/attribute.controller.ts

This file was deleted.

Loading
Loading