diff options
Diffstat (limited to 'app/assets/javascripts/observability')
6 files changed, 247 insertions, 10 deletions
diff --git a/app/assets/javascripts/observability/components/observability_app.vue b/app/assets/javascripts/observability/components/observability_app.vue index 4f5e27be46f..33d23ea043b 100644 --- a/app/assets/javascripts/observability/components/observability_app.vue +++ b/app/assets/javascripts/observability/components/observability_app.vue @@ -1,21 +1,69 @@ <script> +import { darkModeEnabled } from '~/lib/utils/color_utils'; +import { setUrlParams } from '~/lib/utils/url_utility'; + +import { MESSAGE_EVENT_TYPE, OBSERVABILITY_ROUTES, SKELETON_VARIANT } from '../constants'; +import ObservabilitySkeleton from './skeleton/index.vue'; + export default { + components: { + ObservabilitySkeleton, + }, props: { observabilityIframeSrc: { type: String, required: true, }, }, + computed: { + iframeSrcWithParams() { + return setUrlParams( + { theme: darkModeEnabled() ? 'dark' : 'light', username: gon?.current_username }, + this.observabilityIframeSrc, + ); + }, + getSkeletonVariant() { + switch (this.$route.path) { + case OBSERVABILITY_ROUTES.DASHBOARDS: + return SKELETON_VARIANT.DASHBOARDS; + case OBSERVABILITY_ROUTES.EXPLORE: + return SKELETON_VARIANT.EXPLORE; + case OBSERVABILITY_ROUTES.MANAGE: + return SKELETON_VARIANT.MANAGE; + default: + return SKELETON_VARIANT.DASHBOARDS; + } + }, + }, mounted() { window.addEventListener('message', this.messageHandler); }, + destroyed() { + window.removeEventListener('message', this.messageHandler); + }, methods: { messageHandler(e) { const isExpectedOrigin = e.origin === new URL(this.observabilityIframeSrc)?.origin; + if (!isExpectedOrigin) return; - const isNewObservabilityPath = this.$route?.query?.observability_path !== e.data?.url; + const { + data: { type, payload }, + } = e; + switch (type) { + case MESSAGE_EVENT_TYPE.GOUI_LOADED: + this.$refs.iframeSkeleton.handleSkeleton(); + break; + case MESSAGE_EVENT_TYPE.GOUI_ROUTE_UPDATE: + this.routeUpdateHandler(payload); + break; + default: + break; + } + }, + routeUpdateHandler(payload) { + const isNewObservabilityPath = this.$route?.query?.observability_path !== payload?.url; - const shouldNotHandleMessage = !isExpectedOrigin || !e.data.url || !isNewObservabilityPath; + const shouldNotHandleMessage = !payload.url || !isNewObservabilityPath; if (shouldNotHandleMessage) { return; @@ -24,7 +72,7 @@ export default { // this will update the `observability_path` query param on each route change inside Observability UI this.$router.replace({ name: this.$route.pathname, - query: { ...this.$route.query, observability_path: e.data.url }, + query: { ...this.$route.query, observability_path: payload.url }, }); }, }, @@ -32,11 +80,14 @@ export default { </script> <template> - <iframe - id="observability-ui-iframe" - data-testid="observability-ui-iframe" - frameborder="0" - height="100%" - :src="observabilityIframeSrc" - ></iframe> + <observability-skeleton ref="iframeSkeleton" :variant="getSkeletonVariant"> + <iframe + id="observability-ui-iframe" + data-testid="observability-ui-iframe" + frameborder="0" + height="100%" + :src="iframeSrcWithParams" + sandbox="allow-same-origin allow-forms allow-scripts" + ></iframe> + </observability-skeleton> </template> diff --git a/app/assets/javascripts/observability/components/skeleton/dashboards.vue b/app/assets/javascripts/observability/components/skeleton/dashboards.vue new file mode 100644 index 00000000000..8b106407953 --- /dev/null +++ b/app/assets/javascripts/observability/components/skeleton/dashboards.vue @@ -0,0 +1,29 @@ +<script> +import { GlSkeletonLoader } from '@gitlab/ui'; + +export default { + components: { + GlSkeletonLoader, + }, +}; +</script> +<template> + <gl-skeleton-loader :height="200"> + <!-- Top left --> + <rect y="2" width="10" height="8" /> + <rect y="2" x="15" width="15" height="8" /> + <rect y="2" x="35" width="15" height="8" /> + + <!-- Top right --> + <rect y="2" x="354" width="10" height="8" /> + <rect y="2" x="366" width="10" height="8" /> + <rect y="2" x="378" width="10" height="8" /> + <rect y="2" x="390" width="10" height="8" /> + + <!-- Middle header --> + <rect y="15" width="400" height="30" rx="2" ry="2" /> + + <!-- Dashboard container --> + <rect y="50" width="200" height="100" rx="2" ry="2" /> + </gl-skeleton-loader> +</template> diff --git a/app/assets/javascripts/observability/components/skeleton/explore.vue b/app/assets/javascripts/observability/components/skeleton/explore.vue new file mode 100644 index 00000000000..1fcbd4fb1cb --- /dev/null +++ b/app/assets/javascripts/observability/components/skeleton/explore.vue @@ -0,0 +1,27 @@ +<script> +import { GlSkeletonLoader } from '@gitlab/ui'; + +export default { + components: { + GlSkeletonLoader, + }, +}; +</script> +<template> + <gl-skeleton-loader :height="200"> + <!-- Top left --> + <circle y="2" cx="6" cy="6" r="4" /> + <rect y="2" x="15" width="15" height="8" /> + <rect y="2" x="35" width="40" height="8" /> + + <!-- Top right --> + + <rect y="2" x="263" width="13" height="8" /> + <rect y="2" x="278" width="8" height="8" /> + <rect y="2" x="288" width="50" height="8" /> + <rect y="2" x="340" width="18" height="8" /> + <rect y="2" x="360" width="30" height="8" /> + + <rect y="15" width="400" height="30" rx="2" ry="2" /> + </gl-skeleton-loader> +</template> diff --git a/app/assets/javascripts/observability/components/skeleton/index.vue b/app/assets/javascripts/observability/components/skeleton/index.vue new file mode 100644 index 00000000000..1e2671c8166 --- /dev/null +++ b/app/assets/javascripts/observability/components/skeleton/index.vue @@ -0,0 +1,89 @@ +<script> +import { GlSkeletonLoader } from '@gitlab/ui'; +import { SKELETON_VARIANT } from '../../constants'; +import DashboardsSkeleton from './dashboards.vue'; +import ExploreSkeleton from './explore.vue'; +import ManageSkeleton from './manage.vue'; + +export default { + SKELETON_VARIANT, + components: { + GlSkeletonLoader, + DashboardsSkeleton, + ExploreSkeleton, + ManageSkeleton, + }, + props: { + variant: { + type: String, + required: false, + default: '', + }, + }, + data() { + return { + loading: null, + timerId: null, + }; + }, + mounted() { + this.timerId = setTimeout(() => { + /** + * If observability UI is not loaded then this.loading would be null + * we will show skeleton in that case + */ + if (this.loading !== false) { + this.showSkeleton(); + } + }, 500); + }, + methods: { + handleSkeleton() { + if (this.loading === null) { + /** + * If observability UI content loads with in 500ms + * do not show skeleton. + */ + clearTimeout(this.timerId); + return; + } + + /** + * If observability UI content loads after 500ms + * wait for 400ms to hide skeleton. + * This is mostly to avoid the flashing effect If content loads imediately after skeleton + */ + setTimeout(this.hideSkeleton, 400); + }, + hideSkeleton() { + this.loading = false; + }, + showSkeleton() { + this.loading = true; + }, + }, +}; +</script> +<template> + <div class="gl-flex-grow-1 gl-display-flex gl-flex-direction-column gl-flex-align-items-stretch"> + <div v-show="loading" class="gl-px-5"> + <dashboards-skeleton v-if="variant === $options.SKELETON_VARIANT.DASHBOARDS" /> + <explore-skeleton v-else-if="variant === $options.SKELETON_VARIANT.EXPLORE" /> + <manage-skeleton v-else-if="variant === $options.SKELETON_VARIANT.MANAGE" /> + + <gl-skeleton-loader v-else> + <rect y="2" width="10" height="8" /> + <rect y="2" x="15" width="15" height="8" /> + <rect y="2" x="35" width="15" height="8" /> + <rect y="15" width="400" height="30" /> + </gl-skeleton-loader> + </div> + + <div + v-show="!loading" + class="gl-flex-grow-1 gl-display-flex gl-flex-direction-column gl-flex-align-items-stretch" + > + <slot></slot> + </div> + </div> +</template> diff --git a/app/assets/javascripts/observability/components/skeleton/manage.vue b/app/assets/javascripts/observability/components/skeleton/manage.vue new file mode 100644 index 00000000000..4b029120328 --- /dev/null +++ b/app/assets/javascripts/observability/components/skeleton/manage.vue @@ -0,0 +1,25 @@ +<script> +import { GlSkeletonLoader } from '@gitlab/ui'; + +export default { + components: { + GlSkeletonLoader, + }, +}; +</script> +<template> + <gl-skeleton-loader :height="200"> + <!-- Top header--> + <rect y="2" width="400" height="30" /> + + <rect y="35" x="65" width="80" height="8" /> + <rect y="35" x="205" width="30" height="8" /> + <rect y="35" x="240" width="25" height="8" /> + <rect y="35" x="270" width="20" height="8" /> + + <rect y="55" x="65" width="100" height="8" /> + <rect y="55" x="225" width="65" height="8" /> + + <rect y="65" x="65" width="225" height="200" rx="2" ry="2" /> + </gl-skeleton-loader> +</template> diff --git a/app/assets/javascripts/observability/constants.js b/app/assets/javascripts/observability/constants.js new file mode 100644 index 00000000000..74dd543e285 --- /dev/null +++ b/app/assets/javascripts/observability/constants.js @@ -0,0 +1,16 @@ +export const MESSAGE_EVENT_TYPE = Object.freeze({ + GOUI_LOADED: 'GOUI_LOADED', + GOUI_ROUTE_UPDATE: 'GOUI_ROUTE_UPDATE', +}); + +export const OBSERVABILITY_ROUTES = Object.freeze({ + DASHBOARDS: '/groups/gitlab-org/-/observability/dashboards', + EXPLORE: '/groups/gitlab-org/-/observability/explore', + MANAGE: '/groups/gitlab-org/-/observability/manage', +}); + +export const SKELETON_VARIANT = Object.freeze({ + DASHBOARDS: 'dashboards', + EXPLORE: 'explore', + MANAGE: 'manage', +}); |