Commit 3510cbd9 authored by Doug Stull's avatar Doug Stull Committed by Phil Hughes

Add state/province selector to trials

- for canada and US

Changelog: added
EE: true
parent e3fd22d4
...@@ -16,6 +16,7 @@ import countriesQuery from 'ee/subscriptions/graphql/queries/countries.query.gra ...@@ -16,6 +16,7 @@ import countriesQuery from 'ee/subscriptions/graphql/queries/countries.query.gra
import statesQuery from 'ee/subscriptions/graphql/queries/states.query.graphql'; import statesQuery from 'ee/subscriptions/graphql/queries/states.query.graphql';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
import { import {
COUNTRIES_WITH_STATES_ALLOWED,
LEADS_COMPANY_NAME_LABEL, LEADS_COMPANY_NAME_LABEL,
LEADS_COMPANY_SIZE_LABEL, LEADS_COMPANY_SIZE_LABEL,
LEADS_COUNTRY_LABEL, LEADS_COUNTRY_LABEL,
...@@ -26,7 +27,6 @@ import { ...@@ -26,7 +27,6 @@ import {
companySizes, companySizes,
} from 'ee/vue_shared/leads/constants'; } from 'ee/vue_shared/leads/constants';
import { import {
COUNTRIES_WITH_STATES_ALLOWED,
PQL_COMPANY_SIZE_PROMPT, PQL_COMPANY_SIZE_PROMPT,
PQL_PHONE_DESCRIPTION, PQL_PHONE_DESCRIPTION,
PQL_STATE_LABEL, PQL_STATE_LABEL,
......
...@@ -21,5 +21,3 @@ export const PQL_HAND_RAISE_ACTION_ERROR = s__( ...@@ -21,5 +21,3 @@ export const PQL_HAND_RAISE_ACTION_ERROR = s__(
export const PQL_HAND_RAISE_ACTION_SUCCESS = s__( export const PQL_HAND_RAISE_ACTION_SUCCESS = s__(
'PQL|Thank you for reaching out! Our sales team will get back to you soon.', 'PQL|Thank you for reaching out! Our sales team will get back to you soon.',
); );
export const COUNTRIES_WITH_STATES_ALLOWED = ['US', 'CA'];
<script> <script>
import { GlFormGroup, GlFormSelect } from '@gitlab/ui'; import { GlFormGroup, GlFormSelect } from '@gitlab/ui';
import countriesQuery from 'ee/subscriptions/graphql/queries/countries.query.graphql'; import countriesQuery from 'ee/subscriptions/graphql/queries/countries.query.graphql';
import { LEADS_COUNTRY_LABEL, LEADS_COUNTRY_PROMPT } from 'ee/vue_shared/leads/constants'; import statesQuery from 'ee/subscriptions/graphql/queries/states.query.graphql';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
import {
COUNTRIES_WITH_STATES_ALLOWED,
LEADS_COUNTRY_LABEL,
LEADS_COUNTRY_PROMPT,
} from 'ee/vue_shared/leads/constants';
import { TRIAL_STATE_LABEL, TRIAL_STATE_PROMPT } from '../constants';
export default { export default {
name: 'CountryOrRegionList', name: 'CountryOrRegionSelector',
components: { components: {
GlFormGroup, GlFormGroup,
GlFormSelect, GlFormSelect,
}, },
directives: {
autofocusonshow,
},
inject: ['user'], inject: ['user'],
data() { data() {
return { ...this.user, countries: [] }; return { ...this.user, countries: [], states: [] };
}, },
i18n: { i18n: {
countryLabel: LEADS_COUNTRY_LABEL, countryLabel: LEADS_COUNTRY_LABEL,
countrySelectPrompt: LEADS_COUNTRY_PROMPT, countrySelectPrompt: LEADS_COUNTRY_PROMPT,
stateLabel: TRIAL_STATE_LABEL,
stateSelectPrompt: TRIAL_STATE_PROMPT,
}, },
computed: { computed: {
countryOptionsWithDefault() { countryOptionsWithDefault() {
...@@ -27,16 +39,43 @@ export default { ...@@ -27,16 +39,43 @@ export default {
...this.countries, ...this.countries,
]; ];
}, },
mustEnterState() {
return COUNTRIES_WITH_STATES_ALLOWED.includes(this.country);
},
showState() {
return !this.$apollo.loading.states && this.states && this.country && this.mustEnterState;
},
stateOptionsWithDefault() {
return [
{
name: this.$options.i18n.stateSelectPrompt,
id: null,
},
...this.states,
];
},
}, },
apollo: { apollo: {
countries: { countries: {
query: countriesQuery, query: countriesQuery,
}, },
states: {
query: statesQuery,
skip() {
return !this.country;
},
variables() {
return {
countryId: this.country,
};
},
},
}, },
}; };
</script> </script>
<template> <template>
<div>
<gl-form-group <gl-form-group
v-if="!$apollo.loading.countries" v-if="!$apollo.loading.countries"
:label="$options.i18n.countryLabel" :label="$options.i18n.countryLabel"
...@@ -45,7 +84,7 @@ export default { ...@@ -45,7 +84,7 @@ export default {
> >
<gl-form-select <gl-form-select
id="country" id="country"
:value="country" v-model="country"
name="country" name="country"
:options="countryOptionsWithDefault" :options="countryOptionsWithDefault"
value-field="id" value-field="id"
...@@ -55,4 +94,23 @@ export default { ...@@ -55,4 +94,23 @@ export default {
required required
/> />
</gl-form-group> </gl-form-group>
<gl-form-group
v-if="showState"
:label="$options.i18n.stateLabel"
label-size="sm"
label-for="state"
>
<gl-form-select
id="state"
v-autofocusonshow
:value="state"
name="state"
:options="stateOptionsWithDefault"
value-field="id"
text-field="name"
data-testid="state"
required
/>
</gl-form-group>
</div>
</template> </template>
...@@ -3,3 +3,5 @@ import { s__, __ } from '~/locale'; ...@@ -3,3 +3,5 @@ import { s__, __ } from '~/locale';
export const TRIAL_FORM_SUBMIT_TEXT = s__('Trial|Continue'); export const TRIAL_FORM_SUBMIT_TEXT = s__('Trial|Continue');
export const TRIAL_COMPANY_SIZE_PROMPT = __('Please select'); export const TRIAL_COMPANY_SIZE_PROMPT = __('Please select');
export const TRIAL_PHONE_DESCRIPTION = s__('Trial|Allowed characters: +, 0-9, -, and spaces.'); export const TRIAL_PHONE_DESCRIPTION = s__('Trial|Allowed characters: +, 0-9, -, and spaces.');
export const TRIAL_STATE_LABEL = __('State/Province');
export const TRIAL_STATE_PROMPT = __('Please select');
...@@ -12,6 +12,7 @@ export const initTrialCreateLeadForm = () => { ...@@ -12,6 +12,7 @@ export const initTrialCreateLeadForm = () => {
companyName, companyName,
companySize, companySize,
country, country,
state,
phoneNumber, phoneNumber,
} = el.dataset; } = el.dataset;
...@@ -25,6 +26,7 @@ export const initTrialCreateLeadForm = () => { ...@@ -25,6 +26,7 @@ export const initTrialCreateLeadForm = () => {
companyName, companyName,
companySize: companySize || null, companySize: companySize || null,
country: country || null, country: country || null,
state: state || null,
phoneNumber, phoneNumber,
}, },
submitPath, submitPath,
......
...@@ -8,6 +8,8 @@ export const LEADS_PHONE_NUMBER_LABEL = __('Telephone number'); ...@@ -8,6 +8,8 @@ export const LEADS_PHONE_NUMBER_LABEL = __('Telephone number');
export const LEADS_COUNTRY_LABEL = __('Country'); export const LEADS_COUNTRY_LABEL = __('Country');
export const LEADS_COUNTRY_PROMPT = __('Please select a country'); export const LEADS_COUNTRY_PROMPT = __('Please select a country');
export const COUNTRIES_WITH_STATES_ALLOWED = ['US', 'CA'];
export const companySizes = Object.freeze([ export const companySizes = Object.freeze([
{ {
name: '1 - 99', name: '1 - 99',
......
...@@ -21,7 +21,7 @@ module EE ...@@ -21,7 +21,7 @@ module EE
first_name: current_user.first_name, first_name: current_user.first_name,
last_name: current_user.last_name, last_name: current_user.last_name,
company_name: current_user.organization company_name: current_user.organization
}.merge(params.slice(:first_name, :last_name, :company_name, :company_size, :phone_number, :country).to_unsafe_h.symbolize_keys) }.merge(params.slice(:first_name, :last_name, :company_name, :company_size, :phone_number, :country, :state).to_unsafe_h.symbolize_keys)
end end
def should_ask_company_question? def should_ask_company_question?
......
...@@ -14,12 +14,15 @@ RSpec.describe 'Trial Capture Lead', :js do ...@@ -14,12 +14,15 @@ RSpec.describe 'Trial Capture Lead', :js do
end end
context 'with valid company information' do context 'with valid company information' do
let(:country) { { id: 'US', name: 'United States of America' } }
let(:extra_trial_params) { { "state" => form_data.dig(:state, :id) } }
let(:form_data) do let(:form_data) do
{ {
phone_number: '+1 23 456-78-90', phone_number: '+1 23 456-78-90',
company_size: '1 - 99', company_size: '1 - 99',
company_name: 'GitLab', company_name: 'GitLab',
country: { id: 'US', name: 'United States of America' } country: country,
state: { id: 'CA', name: 'California' }
} }
end end
...@@ -38,7 +41,7 @@ RSpec.describe 'Trial Capture Lead', :js do ...@@ -38,7 +41,7 @@ RSpec.describe 'Trial Capture Lead', :js do
"gitlab_com_trial" => true, "gitlab_com_trial" => true,
"provider" => "gitlab", "provider" => "gitlab",
"newsletter_segment" => user.email_opted_in "newsletter_segment" => user.email_opted_in
} }.merge(extra_trial_params)
lead_params = { lead_params = {
trial_user: ActionController::Parameters.new(trial_user_params).permit! trial_user: ActionController::Parameters.new(trial_user_params).permit!
...@@ -49,11 +52,13 @@ RSpec.describe 'Trial Capture Lead', :js do ...@@ -49,11 +52,13 @@ RSpec.describe 'Trial Capture Lead', :js do
end end
end end
context 'with state' do
it 'proceeds to the next step' do it 'proceeds to the next step' do
fill_in 'company_name', with: form_data[:company_name] fill_in 'company_name', with: form_data[:company_name]
select form_data[:company_size], from: 'company_size' select form_data[:company_size], from: 'company_size'
fill_in 'phone_number', with: form_data[:phone_number] fill_in 'phone_number', with: form_data[:phone_number]
select form_data.dig(:country, :name), from: 'country' select form_data.dig(:country, :name), from: 'country'
select form_data.dig(:state, :name), from: 'state'
click_button 'Continue' click_button 'Continue'
...@@ -62,6 +67,26 @@ RSpec.describe 'Trial Capture Lead', :js do ...@@ -62,6 +67,26 @@ RSpec.describe 'Trial Capture Lead', :js do
end end
end end
context 'without state' do
let(:country) { { id: 'AF', name: 'Afghanistan' } }
let(:extra_trial_params) { {} }
it 'proceeds to the next step' do
fill_in 'company_name', with: form_data[:company_name]
select form_data[:company_size], from: 'company_size'
fill_in 'phone_number', with: form_data[:phone_number]
select form_data.dig(:country, :name), from: 'country'
expect(page).not_to have_selector('[data-testid="state"]')
click_button 'Continue'
expect(page).not_to have_css('flash-container')
expect(current_path).to eq(select_trials_path)
end
end
end
context 'with phone number validations' do context 'with phone number validations' do
before do before do
fill_in 'company_name', with: 'GitLab' fill_in 'company_name', with: 'GitLab'
......
import VueApollo from 'vue-apollo';
import { createLocalVue } from '@vue/test-utils'; import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import CountryOrRegionList from 'ee/trials/components/country_or_region_selector.vue'; import CountryOrRegionSelector from 'ee/trials/components/country_or_region_selector.vue';
import { countries, states } from '../../hand_raise_leads/components/mock_data';
import { formData } from './mock_data'; import { formData } from './mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
describe('CountryOrRegionList', () => { describe('CountryOrRegionSelector', () => {
let wrapper; let wrapper;
const createComponent = ({ mountFunction = shallowMountExtended } = {}) => { const createComponent = ({ mountFunction = shallowMountExtended } = {}) => {
...@@ -17,10 +18,13 @@ describe('CountryOrRegionList', () => { ...@@ -17,10 +18,13 @@ describe('CountryOrRegionList', () => {
countries() { countries() {
return [{ id: 'US', name: 'United States' }]; return [{ id: 'US', name: 'United States' }];
}, },
states() {
return [{ countryId: 'US', id: 'CA', name: 'California' }];
},
}, },
}; };
return mountFunction(CountryOrRegionList, { return mountFunction(CountryOrRegionSelector, {
localVue, localVue,
apolloProvider: createMockApollo([], mockResolvers), apolloProvider: createMockApollo([], mockResolvers),
provide: { provide: {
...@@ -43,14 +47,38 @@ describe('CountryOrRegionList', () => { ...@@ -43,14 +47,38 @@ describe('CountryOrRegionList', () => {
it.each` it.each`
testid | value testid | value
${'country'} | ${'US'} ${'country'} | ${'US'}
${'state'} | ${'CA'}
`('has the default injected value for $testid', ({ testid, value }) => { `('has the default injected value for $testid', ({ testid, value }) => {
expect(findFormInput(testid).attributes('value')).toBe(value); expect(findFormInput(testid).attributes('value')).toBe(value);
}); });
it('has the correct form input in the form content', () => { it('has the correct form input in the form content', () => {
const visibleFields = ['country']; const visibleFields = ['country', 'state'];
visibleFields.forEach((f) => expect(wrapper.findByTestId(f).exists()).toBe(true)); visibleFields.forEach((f) => expect(wrapper.findByTestId(f).exists()).toBe(true));
}); });
}); });
describe.each`
country | display
${'US'} | ${true}
${'CA'} | ${true}
${'NL'} | ${false}
`('Country & State handling', ({ country, display }) => {
describe(`when provided country is set to ${country}`, () => {
beforeEach(() => {
wrapper = createComponent();
});
it(`should${display ? '' : ' not'} render the state`, async () => {
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
wrapper.setData({ countries, states, country });
await wrapper.vm.$nextTick();
expect(findFormInput('state').exists()).toBe(display);
});
});
});
}); });
...@@ -6,4 +6,5 @@ export const formData = { ...@@ -6,4 +6,5 @@ export const formData = {
companySize: '1-99', companySize: '1-99',
phoneNumber: '192919', phoneNumber: '192919',
country: 'US', country: 'US',
state: 'CA',
}; };
...@@ -17,7 +17,8 @@ RSpec.describe EE::TrialHelper do ...@@ -17,7 +17,8 @@ RSpec.describe EE::TrialHelper do
company_name: '_params_company_name_', company_name: '_params_company_name_',
company_size: '_company_size_', company_size: '_company_size_',
phone_number: '1234', phone_number: '1234',
country: '_country_' country: '_country_',
state: '_state_'
} }
end end
......
...@@ -34003,6 +34003,9 @@ msgstr "" ...@@ -34003,6 +34003,9 @@ msgstr ""
msgid "State your message to activate" msgid "State your message to activate"
msgstr "" msgstr ""
msgid "State/Province"
msgstr ""
msgid "State/Province/City" msgid "State/Province/City"
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