# frozen_string_literal: true module Ariadne # This component is part of navigation components such as `Ariadne::TabContainerComponent`. # and `Ariadne::TabNavComponent` and should not be used by itself. # # @accessibility # `TabComponent` renders the selected anchor tab with `aria-current="page"` by default. # When the selected tab does not correspond to the current page, such as in a nested inner tab, make sure to use aria-current="true" class TabComponent < Ariadne::Component DEFAULT_ARIA_CURRENT_FOR_ANCHOR = :page ARIA_CURRENT_OPTIONS_FOR_ANCHOR = [true, DEFAULT_ARIA_CURRENT_FOR_ANCHOR].freeze # Panel controlled by the Tab. This will not render anything in the tab itself. # It will provide a accessor for the Tab's parent to call and render the panel # content in the appropriate place. # # @param classes [String] <%= link_to_classes_docs %> # @param attributes [Hash] <%= link_to_attributes_docs %> renders_one :panel, lambda { |classes: "", attributes: {}| attributes[:id] = @panel_id tag = :div attributes[:role] ||= :tabpanel attributes[:hidden] = true unless @selected label_present = aria("label", attributes) || aria("labelledby", attributes) unless label_present if @id.present? attributes[:"aria-labelledby"] = @id else raise ArgumentError, "Panels must be labelled. Either set a unique `id` on the tab, or set an `aria-label` directly on the panel" end end Ariadne::BaseComponent.new(tag: tag, classes: classes, attributes: attributes) } # The tab's text. # # @param kwargs [Hash] The same arguments as <%= link_to_component(Ariadne::Text) %>. renders_one :text, Ariadne::Text # TODO: test what hapenns with really long inbox names attr_reader :selected, :id, :classes, :attributes DEFAULT_CLASSES = "ariadne-border-transparent ariadne-text-gray-500 hover:ariadne-text-gray-700 hover:ariadne-border-gray-300 ariadne-whitespace-nowrap ariadne-py-4 ariadne-px-1 ariadne-border-b-2 ariadne-font-medium ariadne-text-sm" # @example Default # # <%= render(Ariadne::TabComponent.new(id: "pub-comment")) do |tab| %> # <% tab.text { "Public comment" } %> # <% tab.panel { "Public comment panel" } %> # <% end %> # @param id [String] The unique ID of the tab. # @param selected [Boolean] Whether the tab is selected or not. # @param with_panel [Boolean] Whether the Tab has an associated panel. # @param href [String] The link to navigate to when the tab is clicked. # @param classes [String] <%= link_to_classes_docs %> # @param attributes [Hash] <%= link_to_attributes_docs %> def initialize(id: nil, selected: false, with_panel: false, href: nil, classes: "", attributes: {}) @id = id @href = href @selected = selected @classes = merge_class_names( DEFAULT_CLASSES, classes, ) @attributes = attributes @attributes[:id] ||= @id if with_panel # TODO: test if @id.blank? raise ArgumentError, "Tabs with panels must have a unique `id`" end @tag = :button @attributes[:type] = :button @attributes[:role] = :tab @panel_id = "panel-#{@id}" @attributes[:"aria-controls"] = @panel_id else @tag = :a end return unless @selected if @tag == :a aria_current = aria("current", attributes) || DEFAULT_ARIA_CURRENT_FOR_ANCHOR @attributes[:"aria-current"] = fetch_or_raise(ARIA_CURRENT_OPTIONS_FOR_ANCHOR, aria_current) else @attributes[:"aria-selected"] = true end end end end