sanger/sequencescape

View on GitHub
app/helpers/deprecation_helper.rb

Summary

Maintainability
A
35 mins
Test Coverage
A
100%
# frozen_string_literal: true

# Groups together tools for deprecating areas of the interface
module DeprecationHelper
  WarningLevel = Struct.new(:style, :title, :icon)

  # Contains the stylings which will be applied to the deprecation warning
  # candidate: We haven't set a date for deprecation yet
  # scheduled: A date has been set, but its more than a week away.
  # imminent: Deprecation is less than a week away
  LEVELS = {
    candidate: WarningLevel.new('info', 'Request for feedback', 'info-circle').freeze,
    scheduled: WarningLevel.new('warning', 'Scheduled for removal in %s days', 'exclamation-circle').freeze,
    imminent: WarningLevel.new('danger', 'Scheduled for removal in %s days', 'exclamation-triangle').freeze
  }.freeze

  # The threshold at which to switch to imminent styling
  IMMINENT_THRESHOLD = 7

  #
  # Renders a card surrounding the enclosed block which informs the user of upcoming deprecation.
  # The user will be presented with a button to let us know they still need the feature, and a
  # link to the alternative if provided. The warning will become increasingly prominent as
  # the date approaches.
  #
  # Once date has been reached, the section will be automatically hidden. It will also log
  # every-time it gets hit to allow for removal.
  #
  # @note When using this feature, tests relying on the deprecated behaviour will begin failing
  #       once the removal date has been hit. While on one level this acts as a nice reminder,
  #       it also unexpectedly breaks the build. Please make sure the tests also get disabled
  #       on the target date. But feel free to spit out a load of warnings to make sure the next
  #       code-removal steps actually happen.
  #
  # @example sample_registration.html.erb
  #  <%= deprecate_section(
  #     date: Date.parse('20190708'),
  #     message: 'Sample registration has been replaced by sample manifests. These provide added features',
  #     replaced_by: sample_manifest_path) do %>
  #     <p>Old we content</p>
  #  <% end %>
  # @param date [nil,Date] The date at which the section will be hidden.
  # @param message [String] Body explaining why the feature is being removed, and where the functionality can be found
  #                         elsewhere.
  # @param replaced_by [String,nil] URL of the replacement (if applicable).
  # @param custom_title [String] Override the title determined by the level.
  # @param custom_style [String] Overide the styles determined by the level.
  # @yield [Void] Yield to block rendering the contents of the card (ie. the feature to be deprecated)
  #
  # @return [String] The HTML to render
  # rubocop:todo Metrics/MethodLength, Metrics/AbcSize
  def deprecate_section(date: nil, message: '', replaced_by: nil, custom_title: nil, custom_style: nil, &block)
    # If we're past the date just hide the section
    if date && Date.current > date
      Rails.logger.warn "Deprecated section past deadline: #{Kernel.caller.first}"
      return ''
    end

    level = _deprecation_level(date)
    remaining = date && (date - Date.current).to_i
    title = (custom_title || level.title) % remaining
    style = custom_style || level.style

    tag.div(class: ['ss-card', "border-#{style}"]) do
      concat(
        tag.div(class: ['card-body', "bg-#{style}", 'text-white']) do
          concat icon('fas', level.icon, title)
          concat tag.p(message)
          concat link_to 'See the alternative', replaced_by, class: %w[btn btn-block btn-outline-light] if replaced_by
          concat mail_to configatron.admin_email,
                         icon('far', 'envelope ', 'Let us know if you still need this'),
                         class: %w[btn btn-block btn-outline-light]
        end
      )
      concat tag.div(class: 'card-body', &block)
    end
  end

  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength

  def _deprecation_level(date)
    if date.nil?
      LEVELS[:candidate]
    elsif (date - Date.current) < IMMINENT_THRESHOLD
      LEVELS[:imminent]
    else
      LEVELS[:scheduled]
    end
  end
end