Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enum translations #442

Merged
merged 9 commits into from
Feb 17, 2023
13 changes: 10 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
version: 2.1

ruby-image: &ruby-image cimg/ruby:<<parameters.ruby-version>>
env-vars: &env-vars
RAILS_ENV: test
NODE_ENV: test
BUNDLE_PATH: vendor/bundle
SPROCKETS: false

orbs:
ruby: circleci/ruby@1.8.0
node: circleci/node@5.0.2
browser-tools: circleci/browser-tools@1.1.3

executors:
Expand All @@ -19,7 +18,7 @@ executors:
default: "2.7"
type: string
docker:
- image: cimg/ruby:<<parameters.ruby-version>>-node
- image: *ruby-image
environment: *env-vars

commands:
Expand All @@ -39,6 +38,7 @@ commands:
cd spec/dummy/
xargs -a Aptfile sudo apt-get install
sudo apt-get install libvips
sudo apt-get install file
- run:
name: Install bundle dependencies
command: |
Expand All @@ -52,6 +52,13 @@ commands:
- run:
name: Setup database
command: (cd spec/dummy && bundle exec rake db:setup)
- run:
name: Install node and yarn
command: |
curl -sL https://deb.nodesource.com/setup_$(cat .node-version).x | sudo -E bash -
sudo apt-get install -y nodejs
curl -o- -sL https://yarnpkg.com/install.sh | bash
sudo ln -s $HOME/.yarn/bin/yarn /usr/local/bin/yarn
- run:
name: Prepare Assets
command: |
Expand Down
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
16
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
--format=doc
--require spec_helper
--color
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Unreleased

#### Changed

* Added translation support for Rails built-in enums in select filters and tag column/row [#442](https://github.com/platanus/activeadmin_addons/pull/442)

### 1.9.0

##### Changed
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Installing this gem will enable the following changes by default:

* The default date input will be `:datepicker` instead of `:date_select`
* Filters and selects will offer integration with [enumerize](https://github.com/brainspec/enumerize)
* Select filters will show translated values when used with Rails built-in `enums`
* All select boxes will use select2

## Addons
Expand Down
16 changes: 16 additions & 0 deletions docs/enum_integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,19 @@ ActiveAdmin.register Invoice do
end
```

## Translation

Tag row and column, as well as the interactive option of the latter, support automatic translation with `I18n` when using Rails built-in `enums`. For this, you just have to define the translations in the correct path in your YAML files:

```yaml
# en.yml
en:
activerecord:
attributes:
invoice:
statuses:
active: Activo
archived: Archivado
```

This is the same structure used for tanslation of select inputs in filters and edit/create forms.
16 changes: 10 additions & 6 deletions lib/activeadmin_addons/addons/tag_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ def select_tag

context.div(interactive_tag_select_params) do
context.select do
possible_values.each do |val|
context.option(value: val, selected: val == data) do
context.text_node val.capitalize
possible_values.each do |label, value|
context.option(value: value, selected: value == data) do
context.text_node label
end
end
end
Expand All @@ -36,7 +36,11 @@ def select_tag
end

def display_data
@enum_attr == :enumerize ? data.text : data
if @enum_attr == :enumerize
data.text
else
EnumUtils.translate_enum_option(model.class, attribute.to_s, data)
end
end

def interactive_params(klass)
Expand Down Expand Up @@ -69,9 +73,9 @@ def interactive_tag_select_params
def possible_values
klass = model.class
if @enum_attr == :enumerize
klass.enumerized_attributes[attribute.to_s].values
klass.enumerized_attributes[attribute.to_s].values.map { |value| [value.capitalize, value] }
else
klass.defined_enums[attribute.to_s].keys
EnumUtils.options_for_select(klass, attribute.to_s)
end
end

Expand Down
17 changes: 17 additions & 0 deletions lib/activeadmin_addons/support/enum_utils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module ActiveAdminAddons
class EnumUtils
def self.options_for_select(klass, enum_name, use_db_value: false)
enum_options_hash = klass.defined_enums[enum_name]
enum_options_hash.map do |enum_option_name, db_value|
value = use_db_value ? db_value : enum_option_name
[translate_enum_option(klass, enum_name, enum_option_name), value]
end
end

def self.translate_enum_option(klass, enum_name, enum_option_name)
klass_key = klass.model_name.i18n_key
key = "activerecord.attributes.#{klass_key}.#{enum_name.pluralize}.#{enum_option_name}"
I18n.t(key, default: enum_option_name)
end
end
end
19 changes: 19 additions & 0 deletions lib/activeadmin_addons/support/select_filter_input_extension.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module ActiveAdminAddons
module SelectFilterInputExtension
def collection_from_enum?
klass.respond_to?(:defined_enums) && klass.defined_enums.has_key?(method.to_s)
end

def collection
if !options[:collection] && collection_from_enum?
EnumUtils.options_for_select(klass, method.to_s, use_db_value: true)
else
super
end
end
end
end

::ActiveAdmin::Inputs::Filters::SelectInput.send(
:prepend, ActiveAdminAddons::SelectFilterInputExtension
)
3 changes: 3 additions & 0 deletions spec/dummy/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ en:
attributes:
invoice:
item_ids: Items
statuses:
active: Activo
archived: Archivado
activeadmin:
addons:
boolean:
Expand Down
6 changes: 6 additions & 0 deletions spec/features/inputs/select2_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
require "rails_helper"

describe "Select 2", type: :feature do
let!(:initial_default_select) { ActiveadminAddons.default_select }

after do
ActiveadminAddons.default_select = initial_default_select
end

it { expect(ActiveadminAddons.default_select).to eq('select2') }

context "when default config is select 2" do
Expand Down
80 changes: 80 additions & 0 deletions spec/features/inputs/select_filter_input_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
require "rails_helper"

describe "Select Filter Input", type: :feature do
context 'when used with an enum column' do
let!(:active_invoice) { Invoice.create!(status: :active) }
let!(:archived_invoice) { Invoice.create!(status: :archived) }
let(:option_selector) { '.filter_form select[name="q[status_eq]"] option' }

context "with collection option" do
before do
register_page(Invoice) do
filter :status, as: :select, collection: [
['Activo, si se considera', 0],
['Archivado, ya no se considera', 1],
['Estado que no existe', 42]
]
end

visit admin_invoices_path
end

it "uses collection directly" do
expect(page.find("#{option_selector}[value='0']").text).to eq('Activo, si se considera')
expect(page.find("#{option_selector}[value='1']").text).to(
eq('Archivado, ya no se considera')
)
expect(page.find("#{option_selector}[value='42']").text).to eq('Estado que no existe')
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be good to test that the collection is actually filtered, what do you think? maybe it's not necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, especially considering that the filter only works with the db value, that way we make sure it's working. I'll add the test soon before merging


it 'displays all records' do
expect(page).to have_css('.col-id', text: /#{active_invoice.id}/)
expect(page).to have_css('.col-id', text: /#{archived_invoice.id}/)
end

context 'when selecting a value', js: true do
before do
pick_select2_entered_option('Activo, si se considera')
click_filter_btn
end

it 'displays only elements with that value' do
expect(page).to have_css('.col-id', text: /#{active_invoice.id}/)
expect(page).not_to have_css('.col-id', text: /#{archived_invoice.id}/)
end
end
end

context "without collection option" do
before do
register_page(Invoice) do
filter :status, as: :select
end

visit admin_invoices_path
end

it "renders options with enum db values and translated labels" do
expect(page.find("#{option_selector}[value='0']").text).to eq('Activo')
expect(page.find("#{option_selector}[value='1']").text).to eq('Archivado')
end

it 'displays all records' do
expect(page).to have_css('.col-id', text: /#{active_invoice.id}/)
expect(page).to have_css('.col-id', text: /#{archived_invoice.id}/)
end

context 'when selecting a value', js: true do
before do
pick_select2_entered_option('Archivado')
click_filter_btn
end

it 'displays only elements with that value' do
expect(page).to have_css('.col-id', text: /#{archived_invoice.id}/)
expect(page).not_to have_css('.col-id', text: /#{active_invoice.id}/)
end
end
end
end
end
83 changes: 67 additions & 16 deletions spec/features/tag_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,22 +102,7 @@
end

context "using Rails Enum" do
context "changing state value" do
before do
register_index(Invoice) do
tag_column :status
end

create_invoice(status: :archived)
visit admin_invoices_path
end

it "shows set value" do
expect(page).to have_css('.archived')
end
end

context 'with interactive option' do
shared_examples 'interactive select rendering' do
let!(:invoice) { create_invoice(status: :active) }

before do
Expand Down Expand Up @@ -161,5 +146,71 @@
expect(page).to have_css('.interactive-tag-select[data-value="active"]')
end
end

context 'with no translations' do
around do |example|
I18n.with_locale('de') do
example.run
end
end

context "without interactive option" do
before do
register_index(Invoice) do
tag_column :status
end

create_invoice(status: :archived)
visit admin_invoices_path
end

it "shows untranslated text as value" do
expect(page.find('td.col-status').text).to eq('Archived')
end
end

context 'with interactive option' do
include_examples 'interactive select rendering'

it 'renders an option for each enum value, with translated text' do
option_selector = '.interactive-tag-select option'
expect(page.find("#{option_selector}[value='active']").text).to eq('active')
expect(page.find("#{option_selector}[value='archived']").text).to eq('archived')
end
end
end

context 'with translations' do
around do |example|
I18n.with_locale('en') do
example.run
end
end

context "without interactive option" do
before do
register_index(Invoice) do
tag_column :status
end

create_invoice(status: :archived)
visit admin_invoices_path
end

it "shows translated text as value" do
expect(page.find('td.col-status').text).to eq('Archivado')
end
end

context 'with interactive option' do
include_examples 'interactive select rendering'

it 'renders an option for each enum value, with translated text' do
option_selector = '.interactive-tag-select option'
expect(page.find("#{option_selector}[value='active']").text).to eq('Activo')
expect(page.find("#{option_selector}[value='archived']").text).to eq('Archivado')
end
end
end
end
end
Loading