Skip to content

Commit

Permalink
Use inline icons as components
Browse files Browse the repository at this point in the history
This makes the icons simple to customize and allows us to deprecate behavior that deals with the internals of the asset pipeline
  • Loading branch information
jcoyne committed May 19, 2022
1 parent d0448fe commit ed84126
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 38 deletions.
1 change: 0 additions & 1 deletion app/assets/images/blacklight/list.svg

This file was deleted.

1 change: 0 additions & 1 deletion app/assets/images/blacklight/search.svg

This file was deleted.

14 changes: 0 additions & 14 deletions app/assets/stylesheets/blacklight/_icons.scss
Original file line number Diff line number Diff line change
@@ -1,17 +1,3 @@
@each $color, $value in $theme-colors {
.btn-#{$color} {
.blacklight-icons svg {
fill: currentColor;
}
}

.btn-outline-#{$color} {
.blacklight-icons svg {
fill: currentColor;
}
}
}

.btn.btn-icon {
padding: $btn-padding-y;

Expand Down
29 changes: 29 additions & 0 deletions app/components/blacklight/icons/legacy_icon_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

module Blacklight
module Icons
class LegacyIconComponent < ::ViewComponent::Base
extend Deprecation

def initialize(name:, classes: '', aria_hidden: false, label: true, role: 'img', additional_options: {})
@name = name
Deprecation.warn(self, "Calling the LegacyIconComponent with \"#{name}\" is deprecated. Instead create a component for this icon.")
@classes = classes
@aria_hidden = aria_hidden
@icon = Blacklight::Icon.new(name, classes: classes, label: label, role: role, additional_options: additional_options)
end

def call
tag.span(svg.html_safe, # rubocop:disable Rails/OutputSafety
class: "blacklight-icons blacklight-icon-#{@name} #{@classes}".strip,
'aria-hidden': (true if @aria_hidden))
end

def svg
Rails.cache.fetch([:blacklight_icon_svg, @name]) do
@icon.svg
end
end
end
end
end
20 changes: 20 additions & 0 deletions app/components/blacklight/icons/list_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module Blacklight
module Icons
# This is the list icon for the search button.
# You can override the default svg by setting:
# Blacklight::Icons::ListComponent.svg = '<svg>your SVG here</svg>'
class ListComponent < ::ViewComponent::Base
def call
svg.html_safe # rubocop:disable Rails/OutputSafety
end

class_attribute :svg, default: <<~SVG
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
</svg>
SVG
end
end
end
20 changes: 20 additions & 0 deletions app/components/blacklight/icons/search_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module Blacklight
module Icons
# This is the magnifing glass icon for the search button.
# You can override the default svg by setting:
# Blacklight::Icons::SearchComponent.svg = '<svg>your SVG here</svg>'
class SearchComponent < ::ViewComponent::Base
def call
svg.html_safe # rubocop:disable Rails/OutputSafety
end

class_attribute :svg, default: <<~SVG
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" d="M0 0h24v24H0V0z"/><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/>
</svg>
SVG
end
end
end
2 changes: 1 addition & 1 deletion app/components/blacklight/search_button_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def initialize(text:, id:)
def call
tag.button(class: 'btn btn-primary search-btn', type: 'submit', id: @id) do
tag.span(@text, class: "submit-search-text") +
blacklight_icon(:search, aria_hidden: true)
render(Blacklight::Icons::SearchComponent.new)
end
end
end
Expand Down
9 changes: 4 additions & 5 deletions app/helpers/blacklight/icon_helper_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ module Blacklight::IconHelperBehavior
# the svg everytime.
# @param [String, Symbol] icon_name
# @return [String]
def blacklight_icon(icon_name, options = {})
Rails.cache.fetch([:blacklight_icons, icon_name, options]) do
icon = Blacklight::Icon.new(icon_name, **options)
tag.span(icon.svg.html_safe, **icon.options)
end
def blacklight_icon(icon_name, _options = {})
render "Blacklight::Icons::#{icon_name.to_s.camelize}Component".constantize.new
rescue NameError
render Blacklight::Icons::LegacyIconComponent.new(name: icon_name)
end
end
7 changes: 4 additions & 3 deletions spec/helpers/blacklight/icon_helper_behavior_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

RSpec.describe Blacklight::IconHelperBehavior do
describe '#blacklight_icon' do
it 'wraps the svg in a span with classes' do
expect(helper.blacklight_icon(:search))
.to have_css 'span.blacklight-icons svg'
subject(:icon) { helper.blacklight_icon(:search) }

it 'returns the svg' do
expect(icon).to have_css 'svg'
end
end
end
38 changes: 25 additions & 13 deletions spec/models/blacklight/icon_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
# frozen_string_literal: true

RSpec.describe Blacklight::Icon do
subject { described_class.new(:search, classes: 'awesome', aria_hidden: true) }
subject(:instance) { described_class.new(:test, classes: 'awesome', aria_hidden: true) }

let(:svg) { String.new(Blacklight::Icons::SearchComponent.new.svg) }
let(:sprockets_asset) { instance_double(Sprockets::Asset, source: svg) }

before do
allow(Rails.application.assets).to receive(:find_asset).and_return(sprockets_asset)
# FileUtils.mkdir_p '.internal_test_app/app/assets/images/blacklight'
# File.write '.internal_test_app/app/assets/images/blacklight/test.svg', Blacklight::Icons::SearchComponent.new.svg
end

describe '#svg' do
it 'returns a string' do
Expand All @@ -20,31 +29,31 @@

it 'adds title' do
expect(Capybara.string(subject.svg))
.to have_css 'title', text: 'Search'
.to have_css 'title', text: 'Test'
end

context 'when label is false' do
subject { described_class.new(:search, classes: 'awesome', aria_hidden: true, label: false) }
subject { described_class.new(:test, classes: 'awesome', aria_hidden: true, label: false) }

it 'does not add title' do
expect(Capybara.string(subject.svg))
.not_to have_css 'title', text: 'Search'
.not_to have_css 'title', text: 'Test'
end
end

context 'with a label context' do
subject { described_class.new(:search, classes: 'awesome', aria_hidden: true, additional_options: { label_context: 'foo' }) }
subject { described_class.new(:test, classes: 'awesome', aria_hidden: true, additional_options: { label_context: 'foo' }) }

it 'adds title' do
expect(Capybara.string(subject.svg))
.to have_css 'title', text: 'Search'
.to have_css 'title', text: 'Test'
end
end
end

describe '#options' do
it 'applies options classes and default class' do
expect(subject.options[:class]).to eq 'blacklight-icons blacklight-icon-search awesome'
expect(subject.options[:class]).to eq 'blacklight-icons blacklight-icon-test awesome'
end

it 'applies options aria-hidden=true' do
Expand All @@ -66,23 +75,26 @@

describe '#path' do
it 'prepends blacklight and sufixes .svg' do
expect(subject.path).to eq 'blacklight/search.svg'
expect(subject.path).to eq 'blacklight/test.svg'
end
end

describe 'file_source' do
subject(:file_source) { instance.file_source }

context 'file is not available' do
subject { described_class.new(:yolo) }

it {
expect { subject.file_source }
.to raise_error(Blacklight::Exceptions::IconNotFound)
}
let(:sprockets_asset) { nil }

it 'raises an error when not found' do
expect { file_source }.to raise_error(Blacklight::Exceptions::IconNotFound)
end
end

context 'file is available' do
it 'returns the filesource' do
expect(subject.file_source).to include '<svg'
expect(file_source).to include '<svg'
end
end
end
Expand Down

0 comments on commit ed84126

Please sign in to comment.