diff --git a/src/directives/tooltip/index.ts b/src/directives/tooltip/index.ts
new file mode 100644
index 0000000000..bba5e6fcfa
--- /dev/null
+++ b/src/directives/tooltip/index.ts
@@ -0,0 +1,4 @@
+import Tooltip from './tooltip';
+
+export { Tooltip };
+export default Tooltip;
diff --git a/src/displays/format-title/index.ts b/src/displays/format-title/index.ts
index 368f628b98..32729f00f2 100644
--- a/src/displays/format-title/index.ts
+++ b/src/displays/format-title/index.ts
@@ -7,4 +7,5 @@ export default defineDisplay({
icon: 'text_format',
handler: handler,
options: null,
+ types: ['string'],
});
diff --git a/src/displays/icon/index.ts b/src/displays/icon/index.ts
index 0a2969bf62..a513d21808 100644
--- a/src/displays/icon/index.ts
+++ b/src/displays/icon/index.ts
@@ -14,4 +14,5 @@ export default defineDisplay(({ i18n }) => ({
width: 'half',
},
],
+ types: ['string'],
}));
diff --git a/src/displays/index.ts b/src/displays/index.ts
index 3dbdf1f672..074ec1614f 100644
--- a/src/displays/index.ts
+++ b/src/displays/index.ts
@@ -1,5 +1,6 @@
import DisplayIcon from './icon/';
import DisplayFormatTitle from './format-title/';
+import DisplayStatusDot from './status-dot/';
-export const displays = [DisplayIcon, DisplayFormatTitle];
+export const displays = [DisplayIcon, DisplayFormatTitle, DisplayStatusDot];
export default displays;
diff --git a/src/displays/status-dot/index.ts b/src/displays/status-dot/index.ts
new file mode 100644
index 0000000000..dbdb0ceb07
--- /dev/null
+++ b/src/displays/status-dot/index.ts
@@ -0,0 +1,11 @@
+import { defineDisplay } from '@/displays/define';
+import DisplayStatusDot from './status-dot.vue';
+
+export default defineDisplay(({ i18n }) => ({
+ id: 'status-dot',
+ name: i18n.t('status_dot'),
+ types: ['status'],
+ icon: 'box',
+ handler: DisplayStatusDot,
+ options: null,
+}));
diff --git a/src/displays/status-dot/readme.md b/src/displays/status-dot/readme.md
new file mode 100644
index 0000000000..df081d6a8a
--- /dev/null
+++ b/src/displays/status-dot/readme.md
@@ -0,0 +1,4 @@
+# Status Dot
+
+Renders the background color as set in the status mapping of the status type interface as a small dot.
+
diff --git a/src/displays/status-dot/status-dot.story.ts b/src/displays/status-dot/status-dot.story.ts
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/displays/status-dot/status-dot.test.ts b/src/displays/status-dot/status-dot.test.ts
new file mode 100644
index 0000000000..7fca8daa5f
--- /dev/null
+++ b/src/displays/status-dot/status-dot.test.ts
@@ -0,0 +1,72 @@
+import DisplayStatusDot from './status-dot.vue';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import VIcon from '@/components/v-icon';
+import VueCompositionAPI from '@vue/composition-api';
+import Tooltip from '@/directives/tooltip';
+
+const localVue = createLocalVue();
+localVue.component('v-icon', VIcon);
+localVue.use(VueCompositionAPI);
+localVue.directive('tooltip', Tooltip);
+
+describe('Displays / Status Dot', () => {
+ it('Renders an empty span if no value is passed', () => {
+ const component = shallowMount(DisplayStatusDot, {
+ localVue,
+ propsData: {
+ value: null,
+ },
+ });
+
+ expect(component.find('span').exists()).toBe(true);
+ expect(component.find('span').text()).toBe('');
+ });
+
+ it('Renders a question mark icon is status is unknown in interface options', () => {
+ const component = shallowMount(DisplayStatusDot, {
+ localVue,
+ propsData: {
+ value: 'draft',
+ interfaceOptions: {
+ status_mapping: {
+ published: {},
+ },
+ },
+ },
+ });
+
+ expect(component.find(VIcon).exists()).toBe(true);
+ expect(component.attributes('name')).toBe('help_outline');
+ });
+
+ it('Renders the dot with the correct color', () => {
+ const component = shallowMount(DisplayStatusDot, {
+ localVue,
+ propsData: {
+ value: 'draft',
+ interfaceOptions: {
+ status_mapping: {
+ draft: {
+ background_color: 'rgb(171, 202, 188)',
+ },
+ },
+ },
+ },
+ });
+
+ expect(component.exists()).toBe(true);
+ expect(component.attributes('style')).toBe('background-color: rgb(171, 202, 188);');
+ });
+
+ it('Sets status to null if interface options are missing', () => {
+ const component = shallowMount(DisplayStatusDot, {
+ localVue,
+ propsData: {
+ value: 'draft',
+ interfaceOptions: null,
+ },
+ });
+
+ expect((component.vm as any).status).toBe(null);
+ });
+});
diff --git a/src/displays/status-dot/status-dot.vue b/src/displays/status-dot/status-dot.vue
new file mode 100644
index 0000000000..03b7191922
--- /dev/null
+++ b/src/displays/status-dot/status-dot.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/displays/types.ts b/src/displays/types.ts
index a551fb96d7..08f70ba752 100644
--- a/src/displays/types.ts
+++ b/src/displays/types.ts
@@ -12,6 +12,7 @@ export type DisplayConfig = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
handler: DisplayHandlerFunction | Component;
options: null | Partial[] | Component;
+ types: string[];
};
export type DisplayContext = { i18n: VueI18n };
diff --git a/src/lang/en-US/index.json b/src/lang/en-US/index.json
index 79934e44b4..b10ea6547a 100644
--- a/src/lang/en-US/index.json
+++ b/src/lang/en-US/index.json
@@ -272,6 +272,8 @@
"select_statuses": "Select Statuses",
+ "status_dot": "Status (Dot)",
+
"about_directus": "About Directus",
"activity_log": "Activity Log",
"add_field_filter": "Add a field filter",
diff --git a/src/layouts/tabular/tabular.vue b/src/layouts/tabular/tabular.vue
index 378a5c660c..825699a509 100644
--- a/src/layouts/tabular/tabular.vue
+++ b/src/layouts/tabular/tabular.vue
@@ -69,13 +69,17 @@
@update:sort="onSortChange"
>
- {{ item[header.value] }}
+
+ {{ item[header.value] }}
+
@@ -331,8 +335,12 @@ export default defineComponent({
localWidths.value[field.field] ||
_viewOptions.value?.widths?.[field.field] ||
null,
- display: field.display,
- display_options: field.display_options,
+ field: {
+ display: field.display,
+ displayOptions: field.display_options,
+ interface: field.interface,
+ interfaceOptions: field.options,
+ },
}));
},
set(val) {
diff --git a/src/views/private/components/render-display/index.ts b/src/views/private/components/render-display/index.ts
index 590ac95877..7efeebffef 100644
--- a/src/views/private/components/render-display/index.ts
+++ b/src/views/private/components/render-display/index.ts
@@ -1,38 +1,4 @@
-import { defineComponent } from '@vue/composition-api';
-import displays from '@/displays';
-import { DisplayHandlerFunction } from '@/displays/types';
+import RenderDisplay from './render-display.vue';
-export default defineComponent({
- props: {
- display: {
- type: String,
- default: null,
- },
- options: {
- type: Object,
- default: () => ({}),
- },
- value: {
- type: [String, Number, Object, Array],
- default: null,
- },
- },
- render(createElement, { props }) {
- const display = displays.find((display) => display.id === props.display);
-
- if (!display) {
- return props.value;
- }
-
- if (typeof display.handler === 'function') {
- return (display.handler as DisplayHandlerFunction)(props.value, props.options);
- }
-
- return createElement(`display-${props.display}`, {
- props: {
- ...props.options,
- value: props.value,
- },
- });
- },
-});
+export { RenderDisplay };
+export default RenderDisplay;
diff --git a/src/views/private/components/render-display/render-display.vue b/src/views/private/components/render-display/render-display.vue
new file mode 100644
index 0000000000..8938c75cdb
--- /dev/null
+++ b/src/views/private/components/render-display/render-display.vue
@@ -0,0 +1,48 @@
+
+ {{ value }}
+
+ {{ display.handler(value, options) }}
+
+
+
+
+