Commit 27c6917b authored by David O'Regan's avatar David O'Regan

Merge branch 'add-text-widget-for-pipeline-wizard' into 'master'

Add the text widget to be used by the Pipeline Wizard

See merge request gitlab-org/gitlab!80176
parents 42dd1dd2 3579d38b
<script>
import { GlFormGroup, GlFormInput } from '@gitlab/ui';
import { uniqueId } from 'lodash';
import { s__ } from '~/locale';
const VALIDATION_STATE = {
NO_VALIDATION: null,
INVALID: false,
VALID: true,
};
export default {
name: 'TextWidget',
components: {
GlFormGroup,
GlFormInput,
},
props: {
label: {
type: String,
required: true,
},
description: {
type: String,
required: false,
default: null,
},
placeholder: {
type: String,
required: false,
default: null,
},
invalidFeedback: {
type: String,
required: false,
default: s__('PipelineWizardInputValidation|This value is not valid'),
},
id: {
type: String,
required: false,
default: () => uniqueId('textWidget-'),
},
pattern: {
type: String,
required: false,
default: null,
},
validate: {
type: Boolean,
required: false,
default: false,
},
required: {
type: Boolean,
required: false,
default: false,
},
default: {
type: String,
required: false,
default: null,
},
},
data() {
return {
touched: false,
value: this.default,
};
},
computed: {
validationState() {
if (!this.showValidationState) return VALIDATION_STATE.NO_VALIDATION;
if (this.isRequiredButEmpty) return VALIDATION_STATE.INVALID;
return this.needsValidationAndPasses ? VALIDATION_STATE.VALID : VALIDATION_STATE.INVALID;
},
showValidationState() {
return this.touched || this.validate;
},
isRequiredButEmpty() {
return this.required && !this.value;
},
needsValidationAndPasses() {
return !this.pattern || new RegExp(this.pattern).test(this.value);
},
invalidFeedbackMessage() {
return this.isRequiredButEmpty
? s__('PipelineWizardInputValidation|This field is required')
: this.invalidFeedback;
},
},
watch: {
validationState(v) {
this.$emit('update:valid', v);
},
value(v) {
this.$emit('input', v.trim());
},
},
created() {
if (this.default) {
this.$emit('input', this.value);
}
},
};
</script>
<template>
<div data-testid="text-widget">
<gl-form-group
:description="description"
:invalid-feedback="invalidFeedbackMessage"
:label="label"
:label-for="id"
:state="validationState"
>
<gl-form-input
:id="id"
v-model="value"
:placeholder="placeholder"
:state="validationState"
type="text"
@blur="touched = true"
/>
</gl-form-group>
</div>
</template>
...@@ -26577,6 +26577,12 @@ msgstr "" ...@@ -26577,6 +26577,12 @@ msgstr ""
msgid "PipelineStatusTooltip|Pipeline: %{ci_status}" msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
msgstr "" msgstr ""
msgid "PipelineWizardInputValidation|This field is required"
msgstr ""
msgid "PipelineWizardInputValidation|This value is not valid"
msgstr ""
msgid "Pipelines" msgid "Pipelines"
msgstr "" msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import { GlFormGroup, GlFormInput } from '@gitlab/ui';
import TextWidget from '~/pipeline_wizard/components/widgets/text.vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
describe('Pipeline Wizard - Text Widget', () => {
const defaultProps = {
label: 'This label',
description: 'some description',
placeholder: 'some placeholder',
pattern: '^[a-z]+$',
invalidFeedback: 'some feedback',
};
let wrapper;
const findGlFormGroup = () => wrapper.findComponent(GlFormGroup);
const findGlFormGroupInvalidFeedback = () => findGlFormGroup().find('.invalid-feedback');
const findGlFormInput = () => wrapper.findComponent(GlFormInput);
const createComponent = (props = {}, mountFn = mountExtended) => {
wrapper = mountFn(TextWidget, {
propsData: {
...defaultProps,
...props,
},
});
};
afterEach(() => {
if (wrapper) {
wrapper.destroy();
}
});
it('creates an input element with the correct label', () => {
createComponent();
expect(wrapper.findByLabelText(defaultProps.label).exists()).toBe(true);
});
it('passes the description', () => {
createComponent({}, shallowMount);
expect(findGlFormGroup().attributes('description')).toBe(defaultProps.description);
});
it('sets the "text" type on the input component', () => {
createComponent();
expect(findGlFormInput().attributes('type')).toBe('text');
});
it('passes the placeholder', () => {
createComponent();
expect(findGlFormInput().attributes('placeholder')).toBe(defaultProps.placeholder);
});
it('emits an update event on input', async () => {
createComponent();
const localValue = 'somevalue';
await findGlFormInput().setValue(localValue);
expect(wrapper.emitted('input')).toEqual([[localValue]]);
});
it('passes invalid feedback message', () => {
createComponent();
expect(findGlFormGroupInvalidFeedback().text()).toBe(defaultProps.invalidFeedback);
});
it('provides invalid feedback', async () => {
createComponent({ validate: true });
await findGlFormInput().setValue('invalid%99');
expect(findGlFormGroup().classes()).toContain('is-invalid');
expect(findGlFormInput().classes()).toContain('is-invalid');
});
it('provides valid feedback', async () => {
createComponent({ validate: true });
await findGlFormInput().setValue('valid');
expect(findGlFormGroup().classes()).toContain('is-valid');
expect(findGlFormInput().classes()).toContain('is-valid');
});
it('does not show validation state when untouched', () => {
createComponent({ value: 'invalid99' });
expect(findGlFormGroup().classes()).not.toContain('is-valid');
expect(findGlFormGroup().classes()).not.toContain('is-invalid');
});
it('shows invalid state on blur', async () => {
createComponent();
await findGlFormInput().setValue('invalid%99');
expect(findGlFormGroup().classes()).not.toContain('is-invalid');
await findGlFormInput().trigger('blur');
expect(findGlFormInput().classes()).toContain('is-invalid');
expect(findGlFormGroup().classes()).toContain('is-invalid');
});
it('shows invalid state when toggling `validate` prop', async () => {
createComponent({
required: true,
validate: false,
});
expect(findGlFormGroup().classes()).not.toContain('is-invalid');
await wrapper.setProps({ validate: true });
expect(findGlFormGroup().classes()).toContain('is-invalid');
});
it('does not update validation if not required', async () => {
createComponent({
pattern: null,
validate: true,
});
expect(findGlFormGroup().classes()).not.toContain('is-invalid');
});
it('sets default value', () => {
const defaultValue = 'foo';
createComponent({
default: defaultValue,
});
expect(wrapper.findByLabelText(defaultProps.label).element.value).toBe(defaultValue);
});
it('emits default value on setup', () => {
const defaultValue = 'foo';
createComponent({
default: defaultValue,
});
expect(wrapper.emitted('input')).toEqual([[defaultValue]]);
});
});
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