`,
@@ -39,6 +53,7 @@ export const basic = () =>
export const multiple = () =>
defineComponent({
+ i18n,
components: { VSelect, RawValue },
props: {
items: {
@@ -47,14 +62,27 @@ export const multiple = () =>
placeholder: {
default: text('Placeholder', 'Enter value...'),
},
+ showDeselect: {
+ default: boolean('Show Deselect', false),
+ },
+ allowOther: {
+ default: boolean('Allow Other', false),
+ },
},
setup() {
const value = ref(null);
return { value };
},
template: `
-
-
+
+
{{ value }}
`,
diff --git a/src/components/v-select/v-select.vue b/src/components/v-select/v-select.vue
index 013ebd5d7f..0f182ef500 100644
--- a/src/components/v-select/v-select.vue
+++ b/src/components/v-select/v-select.vue
@@ -1,10 +1,5 @@
-
+
+
+
+
+
+
+
+
+ {{ multiple ? $t('deselect_all') : $t('deselect') }}
+
+
+
+
+
+
+
+
@@ -38,22 +47,70 @@
/>
+
+
+
+
+
+
+
+
+
+
+ 0 ? $event : null)"
+ />
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('other') }}
+
+
-
+
+
diff --git a/src/components/v-textarea/v-textarea.test.ts b/src/components/v-textarea/v-textarea.test.ts
index bef9e34d9d..e16cc7711d 100644
--- a/src/components/v-textarea/v-textarea.test.ts
+++ b/src/components/v-textarea/v-textarea.test.ts
@@ -21,7 +21,11 @@ describe('Textarea', () => {
await component.vm.$nextTick();
- expect(component.find('.v-textarea').classes()).toEqual(['v-textarea', 'disabled']);
+ expect(component.find('.v-textarea').classes()).toEqual([
+ 'v-textarea',
+ 'disabled',
+ 'full-width',
+ ]);
});
it('Emits just the value for the input event', async () => {
diff --git a/src/components/v-textarea/v-textarea.vue b/src/components/v-textarea/v-textarea.vue
index c470d8189e..b73ccc9e56 100644
--- a/src/components/v-textarea/v-textarea.vue
+++ b/src/components/v-textarea/v-textarea.vue
@@ -36,7 +36,7 @@ export default defineComponent({
},
fullWidth: {
type: Boolean,
- default: false,
+ default: true,
},
value: {
type: String,
diff --git a/src/interfaces/dropdown/dropdown.story.ts b/src/interfaces/dropdown/dropdown.story.ts
new file mode 100644
index 0000000000..eacd172590
--- /dev/null
+++ b/src/interfaces/dropdown/dropdown.story.ts
@@ -0,0 +1,62 @@
+import withPadding from '../../../.storybook/decorators/with-padding';
+import { defineComponent, ref } from '@vue/composition-api';
+import { boolean, withKnobs, text } from '@storybook/addon-knobs';
+import readme from './readme.md';
+import i18n from '@/lang';
+import RawValue from '../../../.storybook/raw-value.vue';
+
+export default {
+ title: 'Interfaces / Dropdown',
+ decorators: [withPadding, withKnobs],
+ parameters: {
+ notes: readme,
+ },
+};
+
+export const basic = () =>
+ defineComponent({
+ components: { RawValue },
+ i18n,
+ props: {
+ allowOther: {
+ default: boolean('Allow Other', false),
+ },
+ allowNone: {
+ default: boolean('Allow None', false),
+ },
+ placeholder: {
+ default: text('Placeholder', 'Select something'),
+ },
+ choices: {
+ default: text(
+ 'Choices',
+ `
+Option A
+Option B
+custom_value::Option C
+trim :: Option D
+`
+ ),
+ },
+ icon: {
+ default: text('Icon', 'person'),
+ },
+ },
+ setup() {
+ const value = ref(null);
+ return { value };
+ },
+ template: `
+
+
+ {{ value }}
+
+ `,
+ });
diff --git a/src/interfaces/dropdown/dropdown.test.ts b/src/interfaces/dropdown/dropdown.test.ts
new file mode 100644
index 0000000000..bd2f86d482
--- /dev/null
+++ b/src/interfaces/dropdown/dropdown.test.ts
@@ -0,0 +1,45 @@
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import VueCompositionAPI from '@vue/composition-api';
+
+import VNotice from '@/components/v-notice';
+import VSelect from '@/components/v-select';
+import VIcon from '@/components/v-icon';
+import InterfaceDropdown from './dropdown.vue';
+import VueI18n from 'vue-i18n';
+import i18n from '@/lang';
+
+const localVue = createLocalVue();
+localVue.use(VueCompositionAPI);
+localVue.use(VueI18n);
+localVue.component('v-select', VSelect);
+localVue.component('v-notice', VNotice);
+localVue.component('v-icon', VIcon);
+
+describe('Interfaces / Dropdown', () => {
+ it('Renders a notice when choices arent set', async () => {
+ const component = shallowMount(InterfaceDropdown, {
+ localVue,
+ i18n,
+ listeners: {
+ input: () => undefined,
+ },
+ });
+ expect(component.find(VNotice).exists()).toBe(true);
+ });
+
+ it('Renders select when choices exist', async () => {
+ const component = shallowMount(InterfaceDropdown, {
+ localVue,
+ i18n,
+ listeners: {
+ input: () => undefined,
+ },
+ propsData: {
+ choices: `
+ test
+ `,
+ },
+ });
+ expect(component.find(VSelect).exists()).toBe(true);
+ });
+});
diff --git a/src/interfaces/dropdown/dropdown.vue b/src/interfaces/dropdown/dropdown.vue
new file mode 100644
index 0000000000..f2f32209d5
--- /dev/null
+++ b/src/interfaces/dropdown/dropdown.vue
@@ -0,0 +1,66 @@
+
+
+ {{ $t('choices_option_configured_incorrectly') }}
+
+
+
+
+
+
+
+
+
diff --git a/src/interfaces/dropdown/index.ts b/src/interfaces/dropdown/index.ts
new file mode 100644
index 0000000000..ccd506d392
--- /dev/null
+++ b/src/interfaces/dropdown/index.ts
@@ -0,0 +1,38 @@
+import { defineInterface } from '@/interfaces/define';
+import InterfaceDropdown from './dropdown.vue';
+
+export default defineInterface(({ i18n }) => ({
+ id: 'dropdown',
+ name: i18n.t('dropdown'),
+ icon: 'arrow_drop_down_circle',
+ component: InterfaceDropdown,
+ options: [
+ {
+ field: 'choices',
+ name: i18n.t('choices'),
+ note: i18n.t('use_double_colon_for_key'),
+ width: 'full',
+ interface: 'textarea',
+ },
+ {
+ field: 'allowOther',
+ name: i18n.t('allow_other'),
+ width: 'half',
+ interface: 'toggle',
+ default_value: false,
+ },
+ {
+ field: 'allowNone',
+ name: i18n.t('allow_none'),
+ width: 'half',
+ interface: 'toggle',
+ default_value: false,
+ },
+ {
+ field: 'icon',
+ name: i18n.t('icon'),
+ width: 'half',
+ interface: 'icon',
+ },
+ ],
+}));
diff --git a/src/interfaces/dropdown/readme.md b/src/interfaces/dropdown/readme.md
new file mode 100644
index 0000000000..fa90b30cfc
--- /dev/null
+++ b/src/interfaces/dropdown/readme.md
@@ -0,0 +1,12 @@
+# Dropdown
+
+Pick one from a list of options.
+
+## Options
+
+| Option | Description | Default |
+|---------------|----------------------------------------|---------|
+| `placeholder` | Text to show when no input is entered | `null` |
+| `allow-none` | Allow the user to deselect the value | `false` |
+| `allow-other` | Allow the user to enter a custom value | `false` |
+| `choices` | What choices to present to the user | `null` |
diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts
index 7acabdb4b9..a56ef6037c 100644
--- a/src/interfaces/index.ts
+++ b/src/interfaces/index.ts
@@ -5,6 +5,7 @@ import InterfaceNumeric from './numeric/';
import InterfaceSlider from './slider/';
import InterfaceToggle from './toggle/';
import InterfaceWYSIWYG from './wysiwyg/';
+import InterfaceDropdown from './dropdown/';
export const interfaces = [
InterfaceTextInput,
@@ -14,6 +15,7 @@ export const interfaces = [
InterfaceDivider,
InterfaceToggle,
InterfaceWYSIWYG,
+ InterfaceDropdown,
];
export default interfaces;
diff --git a/src/interfaces/numeric/numeric.vue b/src/interfaces/numeric/numeric.vue
index b4187693b0..c6008fa90b 100644
--- a/src/interfaces/numeric/numeric.vue
+++ b/src/interfaces/numeric/numeric.vue
@@ -8,7 +8,6 @@
:min="minValue"
:max="maxValue"
:step="stepInterval"
- full-width
@input="$listeners.input"
>
diff --git a/src/interfaces/text-input/text-input.vue b/src/interfaces/text-input/text-input.vue
index b28a7905ca..c2cb901116 100644
--- a/src/interfaces/text-input/text-input.vue
+++ b/src/interfaces/text-input/text-input.vue
@@ -6,7 +6,6 @@
:trim="trim"
:type="masked ? 'password' : 'text'"
:class="font"
- full-width
@input="$listeners.input"
>
diff --git a/src/interfaces/textarea/textarea.vue b/src/interfaces/textarea/textarea.vue
index 8af4b6baa1..96b929ca6d 100644
--- a/src/interfaces/textarea/textarea.vue
+++ b/src/interfaces/textarea/textarea.vue
@@ -5,7 +5,6 @@
:disabled="disabled"
:class="font"
@input="$listeners.input"
- full-width
/>
diff --git a/src/interfaces/toggle/toggle.test.ts b/src/interfaces/toggle/toggle.test.ts
index db2f3d8184..f9b7ee8f53 100644
--- a/src/interfaces/toggle/toggle.test.ts
+++ b/src/interfaces/toggle/toggle.test.ts
@@ -13,6 +13,9 @@ describe('Interfaces / Toggle', () => {
it('Renders a v-checkbox', () => {
const component = shallowMount(InterfaceToggle, {
localVue,
+ listeners: {
+ input: () => undefined,
+ },
});
expect(component.find(VCheckbox).exists()).toBe(true);
diff --git a/src/lang/en-US/index.json b/src/lang/en-US/index.json
index 9dedd657df..80949e08d8 100644
--- a/src/lang/en-US/index.json
+++ b/src/lang/en-US/index.json
@@ -117,6 +117,7 @@
"comments": "Comments",
"item_count": "No Items | One Item | {count} Items",
+ "all_items": "All Items",
"users": "Users",
"files": "Files",
@@ -335,6 +336,17 @@
"custom_formats": "Custom Formats",
"tinymce_options_override": "TinyMCE Options Override",
+ "dropdown": "Dropdown",
+ "allow_other": "Allow Other",
+ "allow_none": "Allow None",
+ "choices": "Choices",
+ "use_double_colon_for_key": "Use double colon for dedicated keys, eg: `value_saved::Option Displayed`",
+ "choices_option_configured_incorrectly": "Choices option configured incorrectly",
+ "deselect": "Deselect",
+ "deselect_all": "Deselect All",
+
+ "other": "Other...",
+
"about_directus": "About Directus",
"activity_log": "Activity Log",
"add_field_filter": "Add a field filter",
@@ -485,7 +497,6 @@
"delete_role_are_you_sure": "Are you sure to delete the role \"{name}\"? This action cannot be undone.",
"desc": "desc",
"description": "Description",
- "deselect": "Deselect",
"dialog_beginning": "Beginning of dialog window.",
"discard_changes": "Discard Changes",
"display_name": "Display Name",
@@ -673,7 +684,6 @@
"operator": "Operator",
"optional": "Optional",
"options": "Options",
- "other": "Other",
"otp": "One-Time Password",
"password": "Password",
"password_reset_sending": "Sending email...",
diff --git a/src/layouts/cards/cards.vue b/src/layouts/cards/cards.vue
index cdacb76142..04b2750e53 100644
--- a/src/layouts/cards/cards.vue
+++ b/src/layouts/cards/cards.vue
@@ -6,7 +6,6 @@
{{ $t('layouts.cards.image_source') }}
{{ $t('layouts.cards.title') }}
-
+
diff --git a/src/layouts/tabular/tabular.vue b/src/layouts/tabular/tabular.vue
index 825699a509..8243771294 100644
--- a/src/layouts/tabular/tabular.vue
+++ b/src/layouts/tabular/tabular.vue
@@ -30,7 +30,6 @@