Commit 1a54abaa authored by charlieablett's avatar charlieablett

Address reviewer comments

- Filter by no weight
- Modify 'board scoped' method
- Use JS objects rather than a series of constants
- Prevent toggling behaviour that leaves dropdown in a weird state
parent 65206e11
# frozen_string_literal: true # frozen_string_literal: true
class DefaultWeightToNil < ActiveRecord::Migration[5.1] class DefaultWeightToNil < ActiveRecord::Migration[5.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false DOWNTIME = false
disable_ddl_transaction!
class Board < ActiveRecord::Base
self.table_name = 'boards'
include ::EachBatch
end
def up def up
Gitlab::BackgroundMigration.steal('UpdateBoardWeightsFromNoneToAny') execute(update_board_weights_query)
Board.where(weight: -1).each_batch(of: 50) do |batch|
range = batch.pluck('MIN(id)', 'MAX(id)').first
say "Setting board weights from None to Any: #{range[0]} - #{range[1]}"
Gitlab::BackgroundMigration::UpdateBoardWeightsFromNoneToAny.new.perform(*range)
end
end end
def down def down
# noop # no-op
end
private
# Only 288 records to update, as of 2019/07/18
def update_board_weights_query
<<~HEREDOC
UPDATE boards
SET weight = NULL
WHERE boards.weight = -1
HEREDOC
end end
end end
<script> <script>
import _ from 'underscore';
import WeightSelect from 'ee/weight_select'; import WeightSelect from 'ee/weight_select';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
const ANY_WEIGHT = { const ANY_WEIGHT = {
LABEL: 'Any Weight', label: __('Any Weight'),
STRING: 'Any', selectValue: 'Any',
VALUE: null, value: null,
valueClass: 'text-secondary',
}; };
const NO_WEIGHT = { const NO_WEIGHT = {
LABEL: 'No Weight', label: __('No Weight'),
STRING: 'None', selectValue: 'None',
VALUE: -1, value: -1,
}; };
function getWeightValue(weight) { function unstringifyValue(value) {
if (Number.isNaN(Number(weight))) { if (!_.isString(value)) {
return weight === NO_WEIGHT.STRING ? NO_WEIGHT.VALUE : ANY_WEIGHT.VALUE; return value;
} }
const numValue = Number(value);
return Number.isNaN(numValue) ? null : numValue;
}
function getWeightValueFromSelect(selectValue) {
switch (selectValue) {
case ANY_WEIGHT.selectValue:
return ANY_WEIGHT.value;
case NO_WEIGHT.selectValue:
return NO_WEIGHT.value;
case null:
case undefined:
return ANY_WEIGHT.value;
default:
return Number(selectValue);
}
}
return parseInt(weight, 10); function getWeightFromValue(strValue) {
const value = unstringifyValue(strValue);
switch (value) {
case ANY_WEIGHT.value:
return ANY_WEIGHT;
case NO_WEIGHT.value:
return NO_WEIGHT;
default:
return {
label: String(value),
selectValue: value,
value,
};
}
} }
export default { export default {
...@@ -34,7 +67,7 @@ export default { ...@@ -34,7 +67,7 @@ export default {
value: { value: {
type: [Number, String], type: [Number, String],
required: false, required: false,
default: ANY_WEIGHT.VALUE, default: ANY_WEIGHT.value,
}, },
canEdit: { canEdit: {
type: Boolean, type: Boolean,
...@@ -52,16 +85,15 @@ export default { ...@@ -52,16 +85,15 @@ export default {
}; };
}, },
computed: { computed: {
selectedWeight() {
return getWeightFromValue(this.value);
},
valueClass() { valueClass() {
if (this.value === ANY_WEIGHT.VALUE) { return this.selectedWeight.valueClass || 'bold';
return 'text-secondary';
}
return 'bold';
}, },
valueText() { valueText() {
if (this.value === NO_WEIGHT.VALUE) return NO_WEIGHT.LABEL; return this.selectedWeight.label;
if (this.value === ANY_WEIGHT.VALUE) return ANY_WEIGHT.LABEL;
return this.value;
}, },
}, },
mounted() { mounted() {
...@@ -73,18 +105,10 @@ export default { ...@@ -73,18 +105,10 @@ export default {
}, },
methods: { methods: {
isSelected(weight) { isSelected(weight) {
if (this.value === NO_WEIGHT.VALUE) { return this.selectedWeight.selectValue === weight;
return weight === NO_WEIGHT.STRING;
}
if (this.value === ANY_WEIGHT.VALUE) {
return weight === ANY_WEIGHT.STRING;
}
return this.value === weight;
}, },
selectWeight(weight) { selectWeight(weight) {
this.board.weight = getWeightValue(weight); this.board.weight = getWeightValueFromSelect(weight);
}, },
}, },
}; };
......
...@@ -110,13 +110,20 @@ class BoardsStoreEE { ...@@ -110,13 +110,20 @@ class BoardsStoreEE {
} }
let { weight } = this.store.boardConfig; let { weight } = this.store.boardConfig;
if (weight !== -1) { if (weight === null) {
if (weight === 0) {
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */ /* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
weight = 'No+Weight'; weight = 'Any';
} else if (weight === -1) {
/* eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings */
weight = 'None';
} else if (weight === 0) {
weight = '0';
} }
if (weight) {
updateFilterPath('weight', weight); updateFilterPath('weight', weight);
this.store.cantEdit.push('weight');
} }
updateFilterPath('assignee_username', this.store.boardConfig.assigneeUsername); updateFilterPath('assignee_username', this.store.boardConfig.assigneeUsername);
if (this.store.boardConfig.assigneeUsername) { if (this.store.boardConfig.assigneeUsername) {
this.store.cantEdit.push('assignee'); this.store.cantEdit.push('assignee');
......
/* eslint-disable prefer-arrow-callback, one-var, no-var, object-shorthand, no-shadow, no-unused-vars, no-else-return, func-names */ /* eslint-disable prefer-arrow-callback, one-var, no-var, object-shorthand, no-unused-vars, no-else-return, func-names */
import $ from 'jquery'; import $ from 'jquery';
import '~/gl_dropdown'; import '~/gl_dropdown';
...@@ -49,13 +49,11 @@ function WeightSelect(els, options = {}) { ...@@ -49,13 +49,11 @@ function WeightSelect(els, options = {}) {
} }
}, },
clicked: function(glDropdownEvt) { clicked: function(glDropdownEvt) {
const { e } = glDropdownEvt; const { e, $el } = glDropdownEvt;
let selected = glDropdownEvt.selectedObj; const selected = $el.data('id');
const inputField = $dropdown.closest('.selectbox').find(`input[name='${fieldName}']`);
if (options.handleClick) { if (options.handleClick) {
e.preventDefault(); e.preventDefault();
selected = inputField.val();
options.handleClick(selected); options.handleClick(selected);
} else if ($dropdown.is('.js-issuable-form-weight')) { } else if ($dropdown.is('.js-issuable-form-weight')) {
e.preventDefault(); e.preventDefault();
......
...@@ -24,7 +24,7 @@ module EE ...@@ -24,7 +24,7 @@ module EE
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_weight(items) def by_weight(items)
return items unless weights? return items unless filtered_by_weight?
return items if filter_by_any_weight? return items if filter_by_any_weight?
if filter_by_no_weight? if filter_by_no_weight?
...@@ -35,7 +35,7 @@ module EE ...@@ -35,7 +35,7 @@ module EE
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def weights? def filtered_by_weight?
params[:weight].present? && params[:weight] != ::Issue::WEIGHT_ALL params[:weight].present? && params[:weight] != ::Issue::WEIGHT_ALL
end end
......
...@@ -6,7 +6,7 @@ module EE ...@@ -6,7 +6,7 @@ module EE
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
# Empty state for milestones and weights. # Empty state for milestones and weights.
EMPTY_SCOPE_STATE = [nil, -1].freeze EMPTY_SCOPE_STATE = [nil].freeze
prepended do prepended do
belongs_to :milestone belongs_to :milestone
......
...@@ -177,7 +177,7 @@ describe 'Scoped issue boards', :js do ...@@ -177,7 +177,7 @@ describe 'Scoped issue boards', :js do
it 'creates board filtering by "None" weight' do it 'creates board filtering by "None" weight' do
create_board_weight('None') create_board_weight('None')
expect(page).to have_selector('.board-card', count: 5) expect(page).to have_selector('.board-card', count: 4)
end end
it 'displays dot highlight and tooltip' do it 'displays dot highlight and tooltip' do
......
...@@ -16,7 +16,7 @@ describe IssuesFinder do ...@@ -16,7 +16,7 @@ describe IssuesFinder do
context 'filter issues with no weight' do context 'filter issues with no weight' do
let(:params) { { weight: Issue::WEIGHT_NONE } } let(:params) { { weight: Issue::WEIGHT_NONE } }
it 'returns all issues with nil weight' do it 'returns all issues with no weight' do
expect(issues).to contain_exactly(issue_with_no_weight, issue1, issue2, issue3, issue4) expect(issues).to contain_exactly(issue_with_no_weight, issue1, issue2, issue3, issue4)
end end
end end
......
...@@ -21,12 +21,16 @@ function activeDropdownItem() { ...@@ -21,12 +21,16 @@ function activeDropdownItem() {
return vm.$el.querySelector('.is-active').innerText.trim(); return vm.$el.querySelector('.is-active').innerText.trim();
} }
function findDropdownItem(text) {
return Array.from(vm.$el.querySelectorAll('li a')).find(({ innerText }) => innerText === text);
}
describe('WeightSelect', () => { describe('WeightSelect', () => {
beforeEach(done => { beforeEach(done => {
setFixtures('<div class="test-container"></div>'); setFixtures('<div class="test-container"></div>');
board = { board = {
weight: 0, weight: -1,
labels: [], labels: [],
}; };
...@@ -118,10 +122,9 @@ describe('WeightSelect', () => { ...@@ -118,10 +122,9 @@ describe('WeightSelect', () => {
it('sets value', done => { it('sets value', done => {
vm.$el.querySelector('.edit-link').click(); vm.$el.querySelector('.edit-link').click();
const dropdownItem = vm.$el.querySelectorAll('li a')[4]; setTimeout(() => {
findDropdownItem('2').click();
expect(dropdownItem.innerText).toBe('2'); });
dropdownItem.click();
setTimeout(() => { setTimeout(() => {
expect(activeDropdownItem()).toEqual('2'); expect(activeDropdownItem()).toEqual('2');
...@@ -135,7 +138,7 @@ describe('WeightSelect', () => { ...@@ -135,7 +138,7 @@ describe('WeightSelect', () => {
vm.$el.querySelector('.edit-link').click(); vm.$el.querySelector('.edit-link').click();
setTimeout(() => { setTimeout(() => {
vm.$el.querySelectorAll('li a')[0].click(); findDropdownItem('Any').click();
}); });
setTimeout(() => { setTimeout(() => {
...@@ -145,12 +148,26 @@ describe('WeightSelect', () => { ...@@ -145,12 +148,26 @@ describe('WeightSelect', () => {
}); });
}); });
it('sets Any Weight if it is already selected', done => {
vm.value = null;
vm.$el.querySelector('.edit-link').click();
setTimeout(() => {
findDropdownItem('Any').click();
});
setTimeout(() => {
expect(board.weight).toEqual(null);
done();
});
});
it('sets No Weight', done => { it('sets No Weight', done => {
vm.value = 2; vm.value = 2;
vm.$el.querySelector('.edit-link').click(); vm.$el.querySelector('.edit-link').click();
setTimeout(() => { setTimeout(() => {
vm.$el.querySelectorAll('li a')[1].click(); findDropdownItem('None').click();
}); });
setTimeout(() => { setTimeout(() => {
...@@ -159,5 +176,19 @@ describe('WeightSelect', () => { ...@@ -159,5 +176,19 @@ describe('WeightSelect', () => {
done(); done();
}); });
}); });
it('sets No Weight if it is already selected', done => {
vm.value = -1;
vm.$el.querySelector('.edit-link').click();
setTimeout(() => {
findDropdownItem('None').click();
});
setTimeout(() => {
expect(board.weight).toEqual(-1);
done();
});
});
}); });
}); });
...@@ -92,19 +92,37 @@ describe Board do ...@@ -92,19 +92,37 @@ describe Board do
stub_licensed_features(scoped_issue_board: true) stub_licensed_features(scoped_issue_board: true)
end end
it 'returns true when milestone is not nil' do it 'returns true when milestone is not "Any milestone" AND is not "No milestone"' do
milestone = create(:milestone) milestone = create(:milestone)
board = create(:board, milestone: milestone, weight: nil, labels: [], assignee: nil) board = create(:board, milestone: milestone, weight: nil, labels: [], assignee: nil)
expect(board).to be_scoped expect(board).to be_scoped
end end
it 'returns true when weight is not nil' do it "returns true when milestone is 'No milestone'" do
board = create(:board, milestone_id: -1, weight: nil, labels: [], assignee: nil)
expect(board).to be_scoped
end
it 'returns true when weight is 0 weight' do
board = create(:board, milestone: nil, weight: 0, labels: [], assignee: nil)
expect(board).to be_scoped
end
it 'returns true when weight is not "Any weight" AND is not "No weight"' do
board = create(:board, milestone: nil, weight: 2, labels: [], assignee: nil) board = create(:board, milestone: nil, weight: 2, labels: [], assignee: nil)
expect(board).to be_scoped expect(board).to be_scoped
end end
it 'returns true when weight is "No weight"' do
board = create(:board, milestone: nil, weight: -1, labels: [], assignee: nil)
expect(board).to be_scoped
end
it 'returns true when any label exists' do it 'returns true when any label exists' do
board = create(:board, milestone: nil, weight: nil, assignee: nil) board = create(:board, milestone: nil, weight: nil, assignee: nil)
board.labels.create!(title: 'foo') board.labels.create!(title: 'foo')
......
...@@ -55,7 +55,7 @@ describe Boards::Issues::CreateService do ...@@ -55,7 +55,7 @@ describe Boards::Issues::CreateService do
expect(issue).to be_valid expect(issue).to be_valid
end end
context 'when board weight is invalid' do context "when board weight is 'none'" do
it 'creates issue with nil weight' do it 'creates issue with nil weight' do
board.update(weight: -1) board.update(weight: -1)
...@@ -65,6 +65,17 @@ describe Boards::Issues::CreateService do ...@@ -65,6 +65,17 @@ describe Boards::Issues::CreateService do
expect(issue).to be_valid expect(issue).to be_valid
end end
end end
context "when board weight is invalid" do
it 'creates issue with nil weight' do
board.update(weight: -6)
issue = service.execute
expect(issue.weight).to be_nil
expect(issue).to be_valid
end
end
end end
end end
......
# frozen_string_literal: true
# rubocop:disable Style/Documentation
module Gitlab
module BackgroundMigration
class UpdateBoardWeightsFromNoneToAny
class Board < ActiveRecord::Base
self.table_name = 'boards'
end
def perform(start_id, stop_id)
Board.where(id: start_id..stop_id).update_all(milestone: nil)
end
end
end
end
...@@ -1501,6 +1501,9 @@ msgstr "" ...@@ -1501,6 +1501,9 @@ msgstr ""
msgid "Any Milestone" msgid "Any Milestone"
msgstr "" msgstr ""
msgid "Any Weight"
msgstr ""
msgid "Any encrypted tokens" msgid "Any encrypted tokens"
msgstr "" msgstr ""
...@@ -9447,6 +9450,9 @@ msgstr "" ...@@ -9447,6 +9450,9 @@ msgstr ""
msgid "No Tag" msgid "No Tag"
msgstr "" msgstr ""
msgid "No Weight"
msgstr ""
msgid "No activities found" msgid "No activities found"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment