Commit 964a603a authored by Martin Wortschack's avatar Martin Wortschack

Merge branch...

Merge branch '220567-implement-token-selector-component-for-group-restrict-membership-by-email-field' into 'master'

Implement token selector component for group "Restrict membership by email" field

See merge request gitlab-org/gitlab!35543
parents 21061935 d285bb3d
...@@ -554,7 +554,7 @@ Some domains cannot be restricted. These are the most popular public email domai ...@@ -554,7 +554,7 @@ Some domains cannot be restricted. These are the most popular public email domai
To enable this feature: To enable this feature:
1. Navigate to the group's **Settings > General** page. 1. Navigate to the group's **Settings > General** page.
1. Expand the **Permissions, LFS, 2FA** section, and enter the domain names into **Restrict membership by email** field. You can enter multiple domains by separating each domain with a comma (,). 1. Expand the **Permissions, LFS, 2FA** section, and enter the domain names into **Restrict membership by email** field.
1. Click **Save changes**. 1. Click **Save changes**.
This will enable the domain-checking for all new users added to the group from this moment on. This will enable the domain-checking for all new users added to the group from this moment on.
......
import Vue from 'vue';
import { __, sprintf } from '~/locale';
import CommaSeparatedListTokenSelector from '../components/comma_separated_list_token_selector.vue';
export default () => {
// eslint-disable-next-line no-new
new Vue({
el: '.js-allowed-email-domains',
components: {
CommaSeparatedListTokenSelector,
},
data() {
const { hiddenInputId, labelId } = document.querySelector(this.$options.el).dataset;
return { hiddenInputId, labelId };
},
render(createElement) {
return createElement('comma-separated-list-token-selector', {
props: {
hiddenInputId: this.hiddenInputId,
ariaLabelledby: this.labelId,
placeholder: __('Enter domain'),
},
scopedSlots: {
'user-defined-token-content': ({ inputText: value }) => {
return sprintf(__('Add "%{value}" to allowlist'), { value });
},
},
});
},
});
};
<script>
import { GlTokenSelector } from '@gitlab/ui';
export default {
name: 'CommaSeparatedListTokenSelector',
hiddenInput: null,
components: { GlTokenSelector },
props: {
hiddenInputId: {
type: String,
required: true,
},
ariaLabelledby: {
type: String,
required: true,
},
placeholder: {
type: String,
required: false,
default: null,
},
},
data() {
return {
selectedTokens: [],
};
},
watch: {
selectedTokens(newValue) {
this.$options.hiddenInput.value = newValue.map(token => token.name).join(',');
// Dispatch `input` event so form submit button becomes active
this.$options.hiddenInput.dispatchEvent(
new Event('input', {
bubbles: true,
cancelable: true,
}),
);
},
},
mounted() {
const hiddenInput = document.getElementById(this.hiddenInputId);
this.$options.hiddenInput = hiddenInput;
if (hiddenInput.value === '') {
return;
}
this.selectedTokens = hiddenInput.value.split(/,\s*/).map((token, index) => ({
id: index,
name: token,
}));
},
methods: {
handleEnter(event) {
// Prevent form from submitting when adding a token
if (event.target.value !== '') {
event.preventDefault();
}
},
},
};
</script>
<template>
<gl-token-selector
v-model="selectedTokens"
container-class="gl-h-auto!"
allow-user-defined-tokens
hide-dropdown-with-no-items
:aria-labelledby="ariaLabelledby"
:placeholder="placeholder"
@keydown.enter="handleEnter"
>
<template #user-defined-token-content="{ inputText }">
<slot name="user-defined-token-content" :input-text="inputText"></slot>
</template>
</gl-token-selector>
</template>
import '~/pages/groups/edit';
import initAllowedEmailDomains from 'ee/groups/settings/allowed_email_domains';
document.addEventListener('DOMContentLoaded', () => {
initAllowedEmailDomains();
});
- return if !group.feature_available?(:group_allowed_email_domains) || group.parent_id.present? - return if !group.feature_available?(:group_allowed_email_domains) || group.parent_id.present?
- hidden_input_id = 'group_allowed_email_domains_list'
%h5= _('Restrict membership by email') - label_id = "#{hidden_input_id}_label"
.form-group .form-group
= f.text_field :allowed_email_domains_list, class: 'form-control', placeholder: _('Enter domain') %label{ id: label_id }
= _('Restrict membership by email domain')
.js-allowed-email-domains{ data: { hidden_input_id: hidden_input_id, label_id: label_id } }
= f.hidden_field :allowed_email_domains_list, id: hidden_input_id
.form-text.text-muted .form-text.text-muted
- read_more_link = link_to(_('Read more'), help_page_path('user/group/index', anchor: 'allowed-domain-restriction-premium')) - read_more_link = link_to(_('Read more'), help_page_path('user/group/index', anchor: 'allowed-domain-restriction-premium'))
= _('Only verified users with an email address in any of these domains can be added to the group.') = _('Only verified users with an email address in any of these domains can be added to the group.')
%br %br
= _('Multiple domains are supported with comma delimiters.') = _('Multiple domains are supported.')
%br
= _('Example: <code>acme.com,acme.co.in,acme.uk</code>.').html_safe
%br %br
= _('Some common domains are not allowed. %{read_more_link}.').html_safe % { read_more_link: read_more_link } = _('Some common domains are not allowed. %{read_more_link}.').html_safe % { read_more_link: read_more_link }
---
title: Change "Restrict membership by email" field from a comma separated list to
the GitLab UI Token Selector component
merge_request: 35543
author:
type: changed
import { nextTick } from 'vue';
import { mount } from '@vue/test-utils';
import { GlToken, GlTokenSelector } from '@gitlab/ui';
import CommaSeparatedListTokenSelector from 'ee/groups/settings/components/comma_separated_list_token_selector.vue';
describe('CommaSeparatedListTokenSelector', () => {
let wrapper;
let div;
let input;
const defaultProps = {
hiddenInputId: 'comma-separated-list',
ariaLabelledby: 'comma-separated-list-label',
};
const createComponent = options => {
wrapper = mount(CommaSeparatedListTokenSelector, {
attachTo: div,
...options,
propsData: {
...defaultProps,
...(options?.propsData || {}),
},
});
};
beforeEach(() => {
div = document.createElement('div');
input = document.createElement('input');
input.setAttribute('type', 'text');
input.id = 'comma-separated-list';
document.body.appendChild(div);
div.appendChild(input);
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
div.remove();
});
describe('when component is mounted', () => {
it.each`
inputValue | expectedTokens
${'gitlab.com,gitlab.org,gitlab.ninja'} | ${['gitlab.com', 'gitlab.org', 'gitlab.ninja']}
${'gitlab.com, gitlab.org, gitlab.ninja'} | ${['gitlab.com', 'gitlab.org', 'gitlab.ninja']}
${'foo bar, baz'} | ${['foo bar', 'baz']}
${'192.168.0.0/24,192.168.1.0/24'} | ${['192.168.0.0/24', '192.168.1.0/24']}
`(
'parses comma separated list ($inputValue) into tokens',
async ({ inputValue, expectedTokens }) => {
input.value = inputValue;
createComponent();
await nextTick();
wrapper.findAll(GlToken).wrappers.forEach((tokenWrapper, index) => {
expect(tokenWrapper.text()).toBe(expectedTokens[index]);
});
},
);
});
describe('when selected tokens changes', () => {
const setup = async () => {
const tokens = [
{
id: 1,
name: 'gitlab.com',
},
{
id: 2,
name: 'gitlab.org',
},
{
id: 3,
name: 'gitlab.ninja',
},
];
createComponent();
await wrapper.setData({
selectedTokens: tokens,
});
};
it('sets input value ', async () => {
await setup();
expect(input.value).toBe('gitlab.com,gitlab.org,gitlab.ninja');
});
it('fires `input` event', async () => {
const dispatchEvent = jest.spyOn(input, 'dispatchEvent');
await setup();
expect(dispatchEvent).toHaveBeenCalledWith(
new Event('input', {
bubbles: true,
cancelable: true,
}),
);
});
});
describe('when enter key is pressed', () => {
it('does not submit the form if token selector text input has a value', async () => {
createComponent();
const tokenSelectorInput = wrapper.find(GlTokenSelector).find('input[type="text"]');
tokenSelectorInput.element.value = 'foo bar';
const event = { preventDefault: jest.fn() };
await tokenSelectorInput.trigger('keydown.enter', event);
expect(event.preventDefault).toHaveBeenCalled();
});
});
});
...@@ -83,7 +83,8 @@ RSpec.describe 'groups/edit.html.haml' do ...@@ -83,7 +83,8 @@ RSpec.describe 'groups/edit.html.haml' do
expect(rendered).to render_template('groups/settings/_allowed_email_domain') expect(rendered).to render_template('groups/settings/_allowed_email_domain')
expect(rendered).to(have_field('group_allowed_email_domains_list', expect(rendered).to(have_field('group_allowed_email_domains_list',
{ disabled: false, { disabled: false,
with: domains.join(",") })) with: domains.join(","),
type: :hidden }))
end end
end end
......
...@@ -1301,6 +1301,9 @@ msgstr "" ...@@ -1301,6 +1301,9 @@ msgstr ""
msgid "Add" msgid "Add"
msgstr "" msgstr ""
msgid "Add \"%{value}\" to allowlist"
msgstr ""
msgid "Add %d issue" msgid "Add %d issue"
msgid_plural "Add %d issues" msgid_plural "Add %d issues"
msgstr[0] "" msgstr[0] ""
...@@ -9373,9 +9376,6 @@ msgstr "" ...@@ -9373,9 +9376,6 @@ msgstr ""
msgid "Exactly one of %{attributes} is required" msgid "Exactly one of %{attributes} is required"
msgstr "" msgstr ""
msgid "Example: <code>acme.com,acme.co.in,acme.uk</code>."
msgstr ""
msgid "Example: @sub\\.company\\.com$" msgid "Example: @sub\\.company\\.com$"
msgstr "" msgstr ""
...@@ -14918,7 +14918,7 @@ msgstr "" ...@@ -14918,7 +14918,7 @@ msgstr ""
msgid "MrDeploymentActions|Stop environment" msgid "MrDeploymentActions|Stop environment"
msgstr "" msgstr ""
msgid "Multiple domains are supported with comma delimiters." msgid "Multiple domains are supported."
msgstr "" msgstr ""
msgid "Multiple issue boards" msgid "Multiple issue boards"
...@@ -19639,7 +19639,7 @@ msgstr "" ...@@ -19639,7 +19639,7 @@ msgstr ""
msgid "Restrict access by IP address" msgid "Restrict access by IP address"
msgstr "" msgstr ""
msgid "Restrict membership by email" msgid "Restrict membership by email domain"
msgstr "" msgstr ""
msgid "Restricts sign-ups for email addresses that match the given regex. See the %{supported_syntax_link_start}supported syntax%{supported_syntax_link_end} for more information." msgid "Restricts sign-ups for email addresses that match the given regex. See the %{supported_syntax_link_start}supported syntax%{supported_syntax_link_end} for more information."
......
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