diff --git a/src/components/v-input/v-input.story.ts b/src/components/v-input/v-input.story.ts index a27a0f54e8..791221b08c 100644 --- a/src/components/v-input/v-input.story.ts +++ b/src/components/v-input/v-input.story.ts @@ -58,7 +58,7 @@ export const monospace = () => ({ export const disabled = () => ``; export const fullWidth = () => ` - + `; export const forceSlug = () => diff --git a/src/components/v-input/v-input.vue b/src/components/v-input/v-input.vue index e1c4a53838..5226115685 100644 --- a/src/components/v-input/v-input.vue +++ b/src/components/v-input/v-input.vue @@ -74,7 +74,7 @@ export default defineComponent({ }, fullWidth: { type: Boolean, - default: false, + default: true, }, value: { type: [String, Number], diff --git a/src/components/v-list/v-list-item.vue b/src/components/v-list/v-list-item.vue index 8de7c8e9de..d04ed0a1b2 100644 --- a/src/components/v-list/v-list-item.vue +++ b/src/components/v-list/v-list-item.vue @@ -132,13 +132,17 @@ export default defineComponent({ background-color: var(--v-list-item-background-color-hover); } - &:not(.disabled):active, - &.active { + &:not(.disabled):active { color: var(--v-list-item-color-active); background-color: var(--v-list-item-background-color-active); } } + &.active { + color: var(--v-list-item-color-active); + background-color: var(--v-list-item-background-color-active); + } + &.disabled { --v-list-item-color: var(--foreground-subdued); } diff --git a/src/components/v-select/readme.md b/src/components/v-select/readme.md index 6aced0b5c4..bec5c125f4 100644 --- a/src/components/v-select/readme.md +++ b/src/components/v-select/readme.md @@ -22,16 +22,17 @@ Renders a dropdown input. ## Props -| Prop | Description | Default | -| ------------- | --------------------------------------------------- | ------- | -| `items`\* | Items to render in the select | | -| `itemText` | What item value to use for the display text | `text` | -| `itemValue` | What item value to use for the item value | `value` | -| `value` | Currently selected item(s) | | -| `multiple` | Allow multiple items to be selected | `false` | -| `placeholder` | What placeholder to show when no items are selected | | -| `full-width` | Render the select at full width | | -| `disabled` | Disable the select | | +| Prop | Description | Default | +|-----------------|-----------------------------------------------------|---------| +| `items`\* | Items to render in the select | | +| `itemText` | What item value to use for the display text | `text` | +| `itemValue` | What item value to use for the item value | `value` | +| `value` | Currently selected item(s) | | +| `multiple` | Allow multiple items to be selected | `false` | +| `placeholder` | What placeholder to show when no items are selected | | +| `full-width` | Render the select at full width | | +| `disabled` | Disable the select | | +| `show-deselect` | Show the deselect option when a value has been set | | ## Events diff --git a/src/components/v-select/v-select.story.ts b/src/components/v-select/v-select.story.ts index 5089c8f889..64b7804b96 100644 --- a/src/components/v-select/v-select.story.ts +++ b/src/components/v-select/v-select.story.ts @@ -1,8 +1,9 @@ import readme from './readme.md'; import { defineComponent, ref } from '@vue/composition-api'; import withPadding from '../../../.storybook/decorators/with-padding'; -import { withKnobs, array, text } from '@storybook/addon-knobs'; +import { withKnobs, array, text, boolean } from '@storybook/addon-knobs'; import RawValue from '../../../.storybook/raw-value.vue'; +import i18n from '@/lang'; import VSelect from './v-select.vue'; @@ -16,6 +17,7 @@ export default { export const basic = () => defineComponent({ + i18n, components: { VSelect, RawValue }, props: { items: { @@ -24,14 +26,26 @@ export const basic = () => 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 }}
`, @@ -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 @@ - + + 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 @@ + + + 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/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') }}
- +
{{ $t('layouts.cards.subtitle') }}
- +
{{ $t('layouts.cards.fallback_icon') }}
- +
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 @@ + {{ $t('cancel') }} diff --git a/src/modules/settings/routes/data-model/collections/components/new-collection/new-collection.vue b/src/modules/settings/routes/data-model/collections/components/new-collection/new-collection.vue index 79bdfe2b7d..36ce1b3813 100644 --- a/src/modules/settings/routes/data-model/collections/components/new-collection/new-collection.vue +++ b/src/modules/settings/routes/data-model/collections/components/new-collection/new-collection.vue @@ -33,17 +33,16 @@

{{ $t('creating_collection_info') }}

{{ $t('name') }}
- +
{{ $t('primary_key_field') }}
- +
{{ $t('type') }}