Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a new widget to manage the addons selection
Based on CWM::MultiStatusSelector, recently added in yast2 4.2.72 See - yast/yast-yast2#1029 - https://github.com/yast/yast-yast2/blob/master/library/cwm/src/lib/cwm/multi_status_selector.rb
- Loading branch information
Showing
3 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
# Copyright (c) [2020] SUSE LLC | ||
# | ||
# All Rights Reserved. | ||
# | ||
# This program is free software; you can redistribute it and/or modify it | ||
# under the terms of version 2 of the GNU General Public License as published | ||
# by the Free Software Foundation. | ||
# | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
# more details. | ||
# | ||
# You should have received a copy of the GNU General Public License along | ||
# with this program; if not, contact SUSE LLC. | ||
# | ||
# To contact SUSE LLC about this file by physical or electronic mail, you may | ||
# find current contact information at www.suse.com. | ||
|
||
require "yast" | ||
require "forwardable" | ||
require "cwm/multi_status_selector" | ||
|
||
module Y2Packager | ||
module Widgets | ||
# A custom widget to display a multi status selector list | ||
class AddonsSelector < CWM::MultiStatusSelector | ||
include Yast::Logger | ||
include Yast::UIShortcuts | ||
|
||
attr_reader :items | ||
|
||
# Constructor | ||
# | ||
# @param products [Array<ProductLocation>] available product locations | ||
# @param preselected_products [Array<ProductLocation>] product locations to be selected | ||
def initialize(products, preselected_products) | ||
@products = products | ||
@items = products.map do |product| | ||
dependencies = product&.details&.depends_on || [] | ||
selected = preselected_products.include?(product) | ||
|
||
Item.new(product, dependencies, selected) | ||
end | ||
end | ||
|
||
# (see CWM::AbstractWidget#contents) | ||
def contents | ||
VBox( | ||
VWeight(60, super), | ||
VWeight( | ||
40, | ||
VBox( | ||
Left(Label("Details (English only)")), | ||
details_widget | ||
) | ||
) | ||
) | ||
end | ||
|
||
# Toggles the item | ||
# | ||
# Also recalculates the dependencies to perform necessary auto selections | ||
# | ||
# @param item [Item] the item to toggle | ||
def toggle(item) | ||
item.toggle | ||
refresh_details(item) | ||
select_dependencies | ||
end | ||
|
||
# Returns selected and auto-selected items | ||
# | ||
# @return [Array<Item>] a collection of selected and auto-selected items | ||
def selected_items | ||
items.select { |i| i.selected? || i.auto_selected? } | ||
end | ||
|
||
# (see CWM::AbstractWidget#contents) | ||
def help | ||
Item.help | ||
end | ||
|
||
private | ||
|
||
# (see CWM::MultiStatusSelector#label_event_handler) | ||
def label_event_handler(item) | ||
refresh_details(item) | ||
end | ||
|
||
# Updates the details area with the given item description | ||
# | ||
# @param item [Item] selected item | ||
def refresh_details(item) | ||
details_widget.value = item.description | ||
end | ||
|
||
# Auto-selects needed dependencies | ||
# | ||
# Based in the current selection, auto selects dependencies not manually | ||
# selected yet. | ||
def select_dependencies | ||
# Resets previous auto selection | ||
@items.select(&:auto_selected?).map(&:unselect!) | ||
|
||
# Recalculates missed dependencies | ||
selected_items = @items.select(&:selected?) | ||
dependencies = selected_items.flat_map(&:dependencies).uniq | ||
missed_dependencies = dependencies - selected_items.map(&:label) | ||
|
||
# Auto-selects them | ||
@items.select { |i| missed_dependencies.include?(i.label) }.each(&:auto_select!) | ||
end | ||
|
||
# Returns the widget to display the details | ||
# | ||
# @return [CWM::RichText] the widget to display the details | ||
def details_widget | ||
@details_widget ||= | ||
begin | ||
w = CWM::RichText.new | ||
w.widget_id = "details_area" | ||
w | ||
end | ||
end | ||
|
||
# Internal class to represent a {Y2Packager::ProductLocation} as selectable item | ||
class Item < Item | ||
include Yast::Logger | ||
include ERB::Util | ||
include Yast::I18n | ||
|
||
# Constructor | ||
# | ||
# @param product [Y2Packager::ProductLocation] the product to be represented | ||
# @param dependencies [Array<String>] a collection with the dependencies ids | ||
# @param selected [Boolean] a flag indicating the initial status for the item | ||
def initialize(product, dependencies, selected) | ||
@product = product | ||
@dependencies = dependencies | ||
@status = selected ? :selected : :unselected | ||
end | ||
|
||
attr_reader :dependencies, :status | ||
|
||
# Returns the item id | ||
# | ||
# @return [String] the item id | ||
def id | ||
product.dir | ||
end | ||
|
||
# Returns the item label | ||
# | ||
# @return [String] the item label | ||
def label | ||
product.summary || product.name | ||
end | ||
|
||
# Builds the item description | ||
def description | ||
@description ||= | ||
begin | ||
erb_file = File.join(__dir__, "product_summary.erb") | ||
log.info "Loading ERB template #{erb_file}" | ||
erb = ERB.new(File.read(erb_file)) | ||
|
||
erb.result(binding) | ||
end | ||
end | ||
|
||
private | ||
|
||
attr_reader :product | ||
end | ||
end | ||
end | ||
end |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
# Copyright (c) [2020] SUSE LLC | ||
# | ||
# All Rights Reserved. | ||
# | ||
# This program is free software; you can redistribute it and/or modify it | ||
# under the terms of version 2 of the GNU General Public License as published | ||
# by the Free Software Foundation. | ||
# | ||
# This program is distributed in the hope that it will be useful, but WITHOUT | ||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
# more details. | ||
# | ||
# You should have received a copy of the GNU General Public License along | ||
# with this program; if not, contact SUSE LLC. | ||
# | ||
# To contact SUSE LLC about this file by physical or electronic mail, you may | ||
# find current contact information at www.suse.com. | ||
|
||
require_relative "../../test_helper" | ||
|
||
require "cwm/rspec" | ||
require "y2packager/product_location" | ||
require "y2packager/product_location_details" | ||
require "y2packager/widgets/addons_selector" | ||
|
||
describe Y2Packager::Widgets::AddonsSelector do | ||
subject(:addons_selector) { described_class.new(products, preselected_products) } | ||
|
||
include_examples "CWM::CustomWidget" | ||
|
||
let(:details_area) do | ||
subject.contents.nested_find { |i| i.is_a?(CWM::RichText) && i.widget_id == "details_area" } | ||
end | ||
|
||
let(:basesystem) do | ||
Y2Packager::ProductLocation.new( | ||
"SLE-15-Module-Basesystem 15.0-0", | ||
"/Basesystem", | ||
product: Y2Packager::ProductLocationDetails.new(product: "sle-module-basesystem") | ||
) | ||
end | ||
|
||
let(:desktop_applications) do | ||
Y2Packager::ProductLocation.new( | ||
"Desktop-Applications-Module 15-0", | ||
"/Desktop-Applications", | ||
product: Y2Packager::ProductLocationDetails.new( | ||
depends_on: ["SLE-15-Module-Basesystem 15.0-0"] | ||
) | ||
) | ||
end | ||
|
||
let(:legacy_product) do | ||
Y2Packager::ProductLocation.new( | ||
"SLE-15-Module-Legacy 15.0-0", | ||
"/Legacy", | ||
product: Y2Packager::ProductLocationDetails.new(product: "sle-module-legacy") | ||
) | ||
end | ||
|
||
let(:products) { [basesystem, desktop_applications, legacy_product] } | ||
let(:preselected_products) { [legacy_product] } | ||
|
||
describe "#initialize" do | ||
it "selects preselected products" do | ||
expect(subject.selected_items.map(&:id)).to eq(preselected_products.map(&:dir)) | ||
end | ||
end | ||
|
||
describe "#items" do | ||
it "returns a collection of items representing available products" do | ||
expect(subject.items.map(&:id)).to eq(products.map(&:dir)) | ||
end | ||
end | ||
|
||
describe "#toggle" do | ||
let(:item) { subject.items.first } | ||
|
||
it "toggles given item" do | ||
expect(item).to receive(:toggle) | ||
|
||
subject.toggle(item) | ||
end | ||
|
||
it "displays the item details" do | ||
expect(details_area).to receive(:value=).with(item.description) | ||
|
||
subject.toggle(item) | ||
end | ||
|
||
context "when selected item has dependencies" do | ||
let(:basesystem_item) { subject.items.find { |i| i.id == "/Basesystem" } } | ||
let(:desktop_apps_item) { subject.items.find { |i| i.id == "/Desktop-Applications" } } | ||
|
||
context "and they are not selected yet" do | ||
it "auto-selects them" do | ||
expect(basesystem_item).to receive(:auto_select!) | ||
|
||
subject.toggle(desktop_apps_item) | ||
end | ||
end | ||
|
||
context "but they are already selected" do | ||
before do | ||
basesystem_item.select! | ||
end | ||
|
||
it "does nothing" do | ||
expect(basesystem_item).to_not receive(:auto_select!) | ||
expect(basesystem_item).to_not receive(:unselect!) | ||
expect(basesystem_item).to_not receive(:select!) | ||
|
||
subject.toggle(desktop_apps_item) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
describe Y2Packager::Widgets::AddonsSelector::Item do | ||
subject(:item) { described_class.new(product, dependencies, selected) } | ||
|
||
let(:product) do | ||
Y2Packager::ProductLocation.new( | ||
"SLE-15-Module-Basesystem 15.0-0", | ||
"/Basesystem", | ||
product: Y2Packager::ProductLocationDetails.new(product: "sle-module-basesystem") | ||
) | ||
end | ||
|
||
let(:dependencies) { nil } | ||
let(:selected) { false } | ||
|
||
describe "#id" do | ||
it "returns a String" do | ||
expect(subject.id).to be_a(String) | ||
end | ||
|
||
it "returns the product dir" do | ||
expect(subject.id).to eq(product.dir) | ||
end | ||
end | ||
|
||
describe "#label" do | ||
let(:product_summary) { "A product summary" } | ||
let(:product_name) { "A product name" } | ||
|
||
before do | ||
allow(product).to receive(:summary).and_return(product_summary) | ||
allow(product).to receive(:name).and_return(product_name) | ||
end | ||
|
||
it "returns a String" do | ||
expect(subject.id).to be_a(String) | ||
end | ||
|
||
context "when product has a summary" do | ||
it "returns the product summary" do | ||
expect(subject.label).to eq(product_summary) | ||
end | ||
end | ||
|
||
context "when product has not a summary" do | ||
let(:product_summary) { nil } | ||
|
||
it "returns the product name" do | ||
expect(subject.label).to eq(product_name) | ||
end | ||
end | ||
end | ||
end |