Commit c9a89e74 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Merge branch '222885-update-design-of-the-container-registry-cleanup-policy-for-tags' into 'master'

Textarea to input and adjust copy of expiration policy

See merge request gitlab-org/gitlab!49104
parents c090533c 57570cb7
<script>
import { GlFormGroup, GlFormTextarea, GlSprintf, GlLink } from '@gitlab/ui';
import { GlFormGroup, GlFormInput, GlSprintf, GlLink } from '@gitlab/ui';
import { NAME_REGEX_LENGTH, TEXT_AREA_INVALID_FEEDBACK } from '../constants';
export default {
components: {
GlFormGroup,
GlFormTextarea,
GlFormInput,
GlSprintf,
GlLink,
},
......@@ -48,7 +48,7 @@ export default {
textAreaLengthErrorMessage() {
return this.isInputValid(this.value) ? '' : TEXT_AREA_INVALID_FEEDBACK;
},
textAreaValidation() {
inputValidation() {
const nameRegexErrors = this.error || this.textAreaLengthErrorMessage;
return {
state: nameRegexErrors === null ? null : !nameRegexErrors,
......@@ -77,8 +77,8 @@ export default {
<gl-form-group
:id="`${name}-form-group`"
:label-for="name"
:state="textAreaValidation.state"
:invalid-feedback="textAreaValidation.message"
:state="inputValidation.state"
:invalid-feedback="inputValidation.message"
>
<template #label>
<span data-testid="label">
......@@ -89,11 +89,11 @@ export default {
</gl-sprintf>
</span>
</template>
<gl-form-textarea
<gl-form-input
:id="name"
v-model="internalValue"
:placeholder="placeholder"
:state="textAreaValidation.state"
:state="inputValidation.state"
:disabled="disabled"
trim
/>
......
......@@ -13,6 +13,16 @@ export default {
required: false,
default: NOT_SCHEDULED_POLICY_TEXT,
},
enabled: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
parsedValue() {
return this.enabled ? this.value : NOT_SCHEDULED_POLICY_TEXT;
},
},
i18n: {
NEXT_CLEANUP_LABEL,
......@@ -26,6 +36,11 @@ export default {
:label="$options.i18n.NEXT_CLEANUP_LABEL"
label-for="expiration-policy-info-text"
>
<gl-form-input id="expiration-policy-info-text" class="gl-pl-0!" plaintext :value="value" />
<gl-form-input
id="expiration-policy-info-text"
class="gl-pl-0!"
plaintext
:value="parsedValue"
/>
</gl-form-group>
</template>
<script>
import { GlFormGroup, GlToggle, GlSprintf } from '@gitlab/ui';
import { ENABLED_TEXT, DISABLED_TEXT, ENABLE_TOGGLE_DESCRIPTION } from '../constants';
import { ENABLED_TOGGLE_DESCRIPTION, DISABLED_TOGGLE_DESCRIPTION } from '../constants';
export default {
components: {
......@@ -20,9 +20,6 @@ export default {
default: false,
},
},
i18n: {
ENABLE_TOGGLE_DESCRIPTION,
},
computed: {
enabled: {
get() {
......@@ -32,8 +29,8 @@ export default {
this.$emit('input', value);
},
},
toggleStatusText() {
return this.enabled ? ENABLED_TEXT : DISABLED_TEXT;
toggleText() {
return this.enabled ? ENABLED_TOGGLE_DESCRIPTION : DISABLED_TOGGLE_DESCRIPTION;
},
},
};
......@@ -44,9 +41,9 @@ export default {
<div class="gl-display-flex">
<gl-toggle id="expiration-policy-toggle" v-model="enabled" :disabled="disabled" />
<span class="gl-ml-5 gl-line-height-24" data-testid="description">
<gl-sprintf :message="$options.i18n.ENABLE_TOGGLE_DESCRIPTION">
<template #toggleStatus>
<strong>{{ toggleStatusText }}</strong>
<gl-sprintf :message="toggleText">
<template #strong="{content}">
<strong>{{ content }}</strong>
</template>
</gl-sprintf>
</span>
......
......@@ -25,7 +25,7 @@ import { formOptionsGenerator } from '~/registry/shared/utils';
import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.graphql';
import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update';
import ExpirationDropdown from './expiration_dropdown.vue';
import ExpirationTextarea from './expiration_textarea.vue';
import ExpirationInput from './expiration_input.vue';
import ExpirationToggle from './expiration_toggle.vue';
import ExpirationRunText from './expiration_run_text.vue';
......@@ -35,7 +35,7 @@ export default {
GlButton,
GlSprintf,
ExpirationDropdown,
ExpirationTextarea,
ExpirationInput,
ExpirationToggle,
ExpirationRunText,
},
......@@ -202,7 +202,11 @@ export default {
data-testid="cadence-dropdown"
@input="onModelChange($event, 'cadence')"
/>
<expiration-run-text :value="prefilledForm.nextRunAt" class="gl-mb-0!" />
<expiration-run-text
:value="prefilledForm.nextRunAt"
:enabled="prefilledForm.enabled"
class="gl-mb-0!"
/>
</div>
<gl-card class="gl-mt-7">
<template #header>
......@@ -229,14 +233,14 @@ export default {
data-testid="keep-n-dropdown"
@input="onModelChange($event, 'keepN')"
/>
<expiration-textarea
<expiration-input
v-model="prefilledForm.nameRegexKeep"
:error="apiErrors.nameRegexKeep"
:disabled="isFieldDisabled"
:label="$options.i18n.NAME_REGEX_KEEP_LABEL"
:description="$options.i18n.NAME_REGEX_KEEP_DESCRIPTION"
name="keep-regex"
data-testid="keep-regex-textarea"
data-testid="keep-regex-input"
@input="onModelChange($event, 'nameRegexKeep')"
@validation="setLocalErrors($event, 'nameRegexKeep')"
/>
......@@ -268,7 +272,7 @@ export default {
data-testid="older-than-dropdown"
@input="onModelChange($event, 'olderThan')"
/>
<expiration-textarea
<expiration-input
v-model="prefilledForm.nameRegex"
:error="apiErrors.nameRegex"
:disabled="isFieldDisabled"
......@@ -276,7 +280,7 @@ export default {
:placeholder="$options.i18n.NAME_REGEX_PLACEHOLDER"
:description="$options.i18n.NAME_REGEX_DESCRIPTION"
name="remove-regex"
data-testid="remove-regex-textarea"
data-testid="remove-regex-input"
@input="onModelChange($event, 'nameRegex')"
@validation="setLocalErrors($event, 'nameRegex')"
/>
......
......@@ -37,11 +37,11 @@ export const NAME_REGEX_DESCRIPTION = s__(
'ContainerRegistry|Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}',
);
export const ENABLED_TEXT = __('Enabled');
export const DISABLED_TEXT = __('Disabled');
export const ENABLE_TOGGLE_DESCRIPTION = s__(
'ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion.',
export const ENABLED_TOGGLE_DESCRIPTION = s__(
'ContainerRegistry|%{strongStart}Enabled%{strongEnd} - Tags that match the rules on this page are automatically scheduled for deletion.',
);
export const DISABLED_TOGGLE_DESCRIPTION = s__(
'ContainerRegistry|%{strongStart}Disabled%{strongEnd} - Tags will not be automatically deleted.',
);
export const CADENCE_LABEL = s__('ContainerRegistry|Run cleanup:');
......
......@@ -7300,13 +7300,16 @@ msgstr[1] ""
msgid "ContainerRegistry|%{imageName} tags"
msgstr ""
msgid "ContainerRegistry|%{title} was successfully scheduled for deletion"
msgid "ContainerRegistry|%{strongStart}Disabled%{strongEnd} - Tags will not be automatically deleted."
msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgid "ContainerRegistry|%{strongStart}Enabled%{strongEnd} - Tags that match the rules on this page are automatically scheduled for deletion."
msgstr ""
msgid "ContainerRegistry|%{title} was successfully scheduled for deletion"
msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags that match the rules on this page are automatically scheduled for deletion."
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
msgid "ContainerRegistry|Build an image"
......
......@@ -84,7 +84,7 @@ RSpec.describe 'Project > Settings > CI/CD > Container registry tag expiration p
within '#js-registry-policies' do
case result
when :available_section
expect(find('[data-testid="enable-toggle"]')).to have_content('Tags that match the rules on this page are automatically scheduled for deletion.')
expect(find('[data-testid="enable-toggle"]')).to have_content('Disabled - Tags will not be automatically deleted.')
when :disabled_message
expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled')
end
......
......@@ -30,8 +30,8 @@ exports[`Settings Form Keep N matches snapshot 1`] = `
`;
exports[`Settings Form Keep Regex matches snapshot 1`] = `
<expiration-textarea-stub
data-testid="keep-regex-textarea"
<expiration-input-stub
data-testid="keep-regex-input"
description="Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
error=""
label="Keep tags matching:"
......@@ -52,8 +52,8 @@ exports[`Settings Form OlderThan matches snapshot 1`] = `
`;
exports[`Settings Form Remove regex matches snapshot 1`] = `
<expiration-textarea-stub
data-testid="remove-regex-textarea"
<expiration-input-stub
data-testid="remove-regex-input"
description="Tags with names that match this regex pattern are removed. %{linkStart}More information%{linkEnd}"
error=""
label="Remove tags matching:"
......
import { shallowMount } from '@vue/test-utils';
import { GlSprintf, GlFormTextarea, GlLink } from '@gitlab/ui';
import { GlSprintf, GlFormInput, GlLink } from '@gitlab/ui';
import { GlFormGroup } from 'jest/registry/shared/stubs';
import component from '~/registry/settings/components/expiration_textarea.vue';
import component from '~/registry/settings/components/expiration_input.vue';
import { NAME_REGEX_LENGTH } from '~/registry/shared/constants';
describe('ExpirationTextarea', () => {
describe('ExpirationInput', () => {
let wrapper;
const defaultProps = {
......@@ -16,7 +16,7 @@ describe('ExpirationTextarea', () => {
const tagsRegexHelpPagePath = 'fooPath';
const findTextArea = () => wrapper.find(GlFormTextarea);
const findInput = () => wrapper.find(GlFormInput);
const findFormGroup = () => wrapper.find(GlFormGroup);
const findLabel = () => wrapper.find('[data-testid="label"]');
const findDescription = () => wrapper.find('[data-testid="description"]');
......@@ -53,7 +53,7 @@ describe('ExpirationTextarea', () => {
it('has a textarea component', () => {
mountComponent();
expect(findTextArea().exists()).toBe(true);
expect(findInput().exists()).toBe(true);
});
it('has a description', () => {
......@@ -78,7 +78,7 @@ describe('ExpirationTextarea', () => {
mountComponent({ value, disabled });
expect(findTextArea().attributes()).toMatchObject({
expect(findInput().attributes()).toMatchObject({
id: defaultProps.name,
value,
placeholder: defaultProps.placeholder,
......@@ -92,7 +92,7 @@ describe('ExpirationTextarea', () => {
mountComponent();
findTextArea().vm.$emit('input', emittedValue);
findInput().vm.$emit('input', emittedValue);
expect(wrapper.emitted('input')).toEqual([[emittedValue]]);
});
});
......@@ -141,12 +141,12 @@ describe('ExpirationTextarea', () => {
// since the component has no state we both emit the event and set the prop
mountComponent({ value: invalidString });
findTextArea().vm.$emit('input', invalidString);
findInput().vm.$emit('input', invalidString);
});
it('textAreaValidation state is false', () => {
expect(findFormGroup().props('state')).toBe(false);
expect(findTextArea().attributes('state')).toBeUndefined();
expect(findInput().attributes('state')).toBeUndefined();
});
it('emits the @validation event with false payload', () => {
......@@ -157,10 +157,10 @@ describe('ExpirationTextarea', () => {
it(`when user input is less than ${NAME_REGEX_LENGTH} state is "true"`, () => {
mountComponent();
findTextArea().vm.$emit('input', 'foo');
findInput().vm.$emit('input', 'foo');
expect(findFormGroup().props('state')).toBe(true);
expect(findTextArea().attributes('state')).toBe('true');
expect(findInput().attributes('state')).toBe('true');
expect(wrapper.emitted('validation')).toEqual([[true]]);
});
});
......
......@@ -28,19 +28,12 @@ describe('ExpirationToggle', () => {
describe('structure', () => {
it('has an input component', () => {
mountComponent();
expect(findInput().exists()).toBe(true);
});
});
describe('model', () => {
it('assigns the right props to the input component', () => {
mountComponent({ value, disabled: true });
expect(findInput().attributes()).toMatchObject({
value,
});
});
it('assigns the right props to the form-group component', () => {
mountComponent();
......@@ -51,16 +44,19 @@ describe('ExpirationToggle', () => {
});
describe('formattedValue', () => {
it('displays the values when it exists', () => {
mountComponent({ value });
expect(findInput().attributes('value')).toBe(value);
});
it('displays a placeholder when no value is present', () => {
mountComponent();
expect(findInput().attributes('value')).toBe(NOT_SCHEDULED_POLICY_TEXT);
});
it.each`
valueProp | enabled | expected
${value} | ${true} | ${value}
${value} | ${false} | ${NOT_SCHEDULED_POLICY_TEXT}
${undefined} | ${false} | ${NOT_SCHEDULED_POLICY_TEXT}
${undefined} | ${true} | ${NOT_SCHEDULED_POLICY_TEXT}
`(
'when value is $valueProp and enabled is $enabled the input value is $expected',
({ valueProp, enabled, expected }) => {
mountComponent({ value: valueProp, enabled });
expect(findInput().attributes('value')).toBe(expected);
},
);
});
});
......@@ -3,9 +3,8 @@ import { GlToggle, GlSprintf } from '@gitlab/ui';
import { GlFormGroup } from 'jest/registry/shared/stubs';
import component from '~/registry/settings/components/expiration_toggle.vue';
import {
ENABLE_TOGGLE_DESCRIPTION,
ENABLED_TEXT,
DISABLED_TEXT,
ENABLED_TOGGLE_DESCRIPTION,
DISABLED_TOGGLE_DESCRIPTION,
} from '~/registry/settings/constants';
describe('ExpirationToggle', () => {
......@@ -39,9 +38,7 @@ describe('ExpirationToggle', () => {
it('has a description', () => {
mountComponent();
expect(findDescription().text()).toContain(
ENABLE_TOGGLE_DESCRIPTION.replace('%{toggleStatus}', ''),
);
expect(findDescription().exists()).toBe(true);
});
});
......@@ -68,13 +65,13 @@ describe('ExpirationToggle', () => {
it('says enabled when the toggle is on', () => {
mountComponent({ value: true });
expect(findDescription().text()).toContain(ENABLED_TEXT);
expect(findDescription().text()).toMatchInterpolatedText(ENABLED_TOGGLE_DESCRIPTION);
});
it('says disabled when the toggle is off', () => {
mountComponent({ value: false });
expect(findDescription().text()).toContain(DISABLED_TEXT);
expect(findDescription().text()).toMatchInterpolatedText(DISABLED_TOGGLE_DESCRIPTION);
});
});
});
......@@ -44,9 +44,9 @@ describe('Settings Form', () => {
const findEnableToggle = () => wrapper.find('[data-testid="enable-toggle"]');
const findCadenceDropdown = () => wrapper.find('[data-testid="cadence-dropdown"]');
const findKeepNDropdown = () => wrapper.find('[data-testid="keep-n-dropdown"]');
const findKeepRegexTextarea = () => wrapper.find('[data-testid="keep-regex-textarea"]');
const findKeepRegexInput = () => wrapper.find('[data-testid="keep-regex-input"]');
const findOlderThanDropdown = () => wrapper.find('[data-testid="older-than-dropdown"]');
const findRemoveRegexTextarea = () => wrapper.find('[data-testid="remove-regex-textarea"]');
const findRemoveRegexInput = () => wrapper.find('[data-testid="remove-regex-input"]');
const mountComponent = ({
props = defaultProps,
......@@ -119,9 +119,9 @@ describe('Settings Form', () => {
${'enabled'} | ${findEnableToggle} | ${'Enable'} | ${'toggle'} | ${false}
${'cadence'} | ${findCadenceDropdown} | ${'Cadence'} | ${'dropdown'} | ${'EVERY_DAY'}
${'keepN'} | ${findKeepNDropdown} | ${'Keep N'} | ${'dropdown'} | ${'TEN_TAGS'}
${'nameRegexKeep'} | ${findKeepRegexTextarea} | ${'Keep Regex'} | ${'textarea'} | ${''}
${'nameRegexKeep'} | ${findKeepRegexInput} | ${'Keep Regex'} | ${'textarea'} | ${''}
${'olderThan'} | ${findOlderThanDropdown} | ${'OlderThan'} | ${'dropdown'} | ${'NINETY_DAYS'}
${'nameRegex'} | ${findRemoveRegexTextarea} | ${'Remove regex'} | ${'textarea'} | ${''}
${'nameRegex'} | ${findRemoveRegexInput} | ${'Remove regex'} | ${'textarea'} | ${''}
`('$fieldName', ({ model, finder, type, defaultValue }) => {
it('matches snapshot', () => {
mountComponent();
......@@ -240,8 +240,8 @@ describe('Settings Form', () => {
await wrapper.vm.$nextTick();
expect(findKeepRegexTextarea().props('error')).toBe('');
expect(findRemoveRegexTextarea().props('error')).toBe('');
expect(findKeepRegexInput().props('error')).toBe('');
expect(findRemoveRegexInput().props('error')).toBe('');
expect(findSaveButton().props('disabled')).toBe(false);
});
});
......@@ -338,7 +338,7 @@ describe('Settings Form', () => {
await waitForPromises();
await wrapper.vm.$nextTick();
expect(findKeepRegexTextarea().props('error')).toEqual('baz');
expect(findKeepRegexInput().props('error')).toEqual('baz');
});
});
});
......
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