Commit 3746c284 authored by Eulyeon Ko's avatar Eulyeon Ko

Disable autocomplete and delay loading datepicker

Without turning off autocomplete,
Chromium-based browsers can fill-in the date input form
triggering an update.

Because we are using `defaultDate` prop to set an initial
date (reactive binding with `value` triggers an update),
we should load GlDatePicker after data fetching is complete.
parent a48b0f16
...@@ -282,10 +282,12 @@ export default { ...@@ -282,10 +282,12 @@ export default {
</template> </template>
<template #default> <template #default>
<gl-datepicker <gl-datepicker
v-if="!isLoading"
ref="datePicker" ref="datePicker"
class="gl-relative" class="gl-relative"
:default-date="parsedDate" :default-date="parsedDate"
show-clear-button show-clear-button
:autocomplete="'off'"
@input="setDate" @input="setDate"
@clear="setDate(null)" @clear="setDate(null)"
/> />
......
---
title: Disable autocomplete for due date in issue sidebar to prevent triggering updates on Chrome
merge_request: 60973
author:
type: fixed
...@@ -5,17 +5,14 @@ require 'spec_helper' ...@@ -5,17 +5,14 @@ require 'spec_helper'
RSpec.describe 'Issue Sidebar' do RSpec.describe 'Issue Sidebar' do
include MobileHelpers include MobileHelpers
let(:group) { create(:group, :nested) } let_it_be(:group) { create(:group, :nested) }
let(:project) { create(:project, :public, namespace: group) } let_it_be(:project) { create(:project, :public, namespace: group) }
let!(:user) { create(:user) } let_it_be(:user) { create(:user) }
let!(:label) { create(:label, project: project, title: 'bug') } let_it_be(:label) { create(:label, project: project, title: 'bug') }
let(:issue) { create(:labeled_issue, project: project, labels: [label]) } let_it_be(:issue) { create(:labeled_issue, project: project, labels: [label]) }
let!(:xss_label) { create(:label, project: project, title: '&lt;script&gt;alert("xss");&lt;&#x2F;script&gt;') } let_it_be(:mock_date) { Date.today.at_beginning_of_month + 2.days }
let!(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) } let_it_be(:issue_with_due_date) { create(:issue, project: project, due_date: mock_date) }
let!(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') } let_it_be(:xss_label) { create(:label, project: project, title: '&lt;script&gt;alert("xss");&lt;&#x2F;script&gt;') }
let!(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) }
let!(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) }
let!(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) }
before do before do
stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab") stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
...@@ -204,7 +201,31 @@ RSpec.describe 'Issue Sidebar' do ...@@ -204,7 +201,31 @@ RSpec.describe 'Issue Sidebar' do
end end
end end
context 'as a allowed user' do context 'due date widget', :js do
let(:due_date_value) { find('[data-testid="due-date"] [data-testid="sidebar-date-value"]') }
context 'when no due date exists' do
before do
visit_issue(project, issue)
end
it "displays 'None'" do
expect(due_date_value.text).to have_content 'None'
end
end
context 'when due date exists' do
before do
visit_issue(project, issue_with_due_date)
end
it "displays the due date" do
expect(due_date_value.text).to have_content mock_date.strftime('%b %-d, %Y')
end
end
end
context 'as an allowed user' do
before do before do
project.add_developer(user) project.add_developer(user)
visit_issue(project, issue) visit_issue(project, issue)
...@@ -238,6 +259,12 @@ RSpec.describe 'Issue Sidebar' do ...@@ -238,6 +259,12 @@ RSpec.describe 'Issue Sidebar' do
end end
context 'editing issue milestone', :js do context 'editing issue milestone', :js do
let_it_be(:milestone_expired) { create(:milestone, project: project, due_date: 5.days.ago) }
let_it_be(:milestone_no_duedate) { create(:milestone, project: project, title: 'Foo - No due date') }
let_it_be(:milestone1) { create(:milestone, project: project, title: 'Milestone-1', due_date: 20.days.from_now) }
let_it_be(:milestone2) { create(:milestone, project: project, title: 'Milestone-2', due_date: 15.days.from_now) }
let_it_be(:milestone3) { create(:milestone, project: project, title: 'Milestone-3', due_date: 10.days.from_now) }
before do before do
page.within('.block.milestone > .title') do page.within('.block.milestone > .title') do
click_on 'Edit' click_on 'Edit'
...@@ -426,6 +453,8 @@ RSpec.describe 'Issue Sidebar' do ...@@ -426,6 +453,8 @@ RSpec.describe 'Issue Sidebar' do
def visit_issue(project, issue) def visit_issue(project, issue)
visit project_issue_path(project, issue) visit project_issue_path(project, issue)
wait_for_requests
end end
def open_issue_sidebar def open_issue_sidebar
......
...@@ -406,6 +406,12 @@ RSpec.describe "Issues > User edits issue", :js do ...@@ -406,6 +406,12 @@ RSpec.describe "Issues > User edits issue", :js do
end end
context 'update due date' do context 'update due date' do
before do
# Due date widget uses GraphQL and needs to wait for requests to come back
# The date picker won't be rendered before requests complete
wait_for_requests
end
it 'adds due date to issue' do it 'adds due date to issue' do
date = Date.today.at_beginning_of_month + 2.days date = Date.today.at_beginning_of_month + 2.days
......
...@@ -80,6 +80,12 @@ describe('Sidebar date Widget', () => { ...@@ -80,6 +80,12 @@ describe('Sidebar date Widget', () => {
expect(findPopoverIcon().exists()).toBe(false); expect(findPopoverIcon().exists()).toBe(false);
}); });
it('does not render GlDatePicker', () => {
createComponent();
expect(findDatePicker().exists()).toBe(false);
});
describe('when issuable has no due date', () => { describe('when issuable has no due date', () => {
beforeEach(async () => { beforeEach(async () => {
createComponent({ createComponent({
...@@ -116,6 +122,11 @@ describe('Sidebar date Widget', () => { ...@@ -116,6 +122,11 @@ describe('Sidebar date Widget', () => {
it('uses a correct prop to set the initial date for GlDatePicker', () => { it('uses a correct prop to set the initial date for GlDatePicker', () => {
expect(findDatePicker().props('value')).toBe(null); expect(findDatePicker().props('value')).toBe(null);
expect(findDatePicker().props('defaultDate')).toEqual(wrapper.vm.parsedDate); expect(findDatePicker().props('defaultDate')).toEqual(wrapper.vm.parsedDate);
expect(findDatePicker().props('autocomplete')).toEqual('off');
});
it('renders GlDatePicker', async () => {
expect(findDatePicker().exists()).toBe(true);
}); });
}); });
......
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