Skip to content

Commit

Permalink
Add source and provider definition tooltips (#3407)
Browse files Browse the repository at this point in the history
* Add source and provider definition

Signed-off-by: Olga Bulat <[email protected]>

* Add suggestions from code review

Signed-off-by: Olga Bulat <[email protected]>

* Rename describedBy

Signed-off-by: Olga Bulat <[email protected]>

---------

Signed-off-by: Olga Bulat <[email protected]>
  • Loading branch information
obulat authored Feb 7, 2024
1 parent f6bd3a5 commit 5475c78
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 35 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/VIconButton/VIconButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export default defineComponent({
* The size of the button, matches the sizes of VButton component.
*/
size: {
type: String as PropType<Exclude<ButtonSize, "disabled">>,
type: String as PropType<ButtonSize>,
required: true,
},
/**
Expand Down
39 changes: 35 additions & 4 deletions frontend/src/components/VMediaInfo/VMetadata.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
<template>
<dl v-if="isSm" class="metadata grid gap-8" :style="columnCount">
<div v-for="datum in metadata" :key="`${datum.label}`">
<dt class="label-regular mb-1 ps-1">{{ $t(datum.label) }}</dt>
<div v-for="datum in metadata" :key="datum.label">
<VSourceProviderTooltip
v-if="tooltipId(datum)"
:described-by="tooltipId(datum)"
class="label-regular mb-1 flex flex-row items-center ps-1"
:datum="datum"
/>
<dt v-else class="label-regular mb-1 flex flex-row ps-1">
{{ $t(datum.label) }}
</dt>
<VMetadataValue
:datum="datum"
@click="sendVisitSourceLinkEvent(datum.source)"
Expand All @@ -10,7 +18,19 @@
</dl>
<dl v-else class="grid grid-cols-[auto,1fr] gap-x-4 gap-y-2">
<template v-for="datum in metadata">
<dt :key="`${datum.label}`" class="label-regular pt-1">
<VSourceProviderTooltip
v-if="tooltipId(datum)"
:key="datum.label"
:described-by="tooltipId(datum)"
class="label-regular flex flex-row items-center p-1 sm:py-0 sm:pe-0"
:datum="datum"
/>
<dt
v-else
:id="datum.label"
:key="datum.label"
class="label-regular flex flex-row pt-1"
>
{{ $t(datum.label) }}
</dt>
<VMetadataValue
Expand All @@ -30,10 +50,11 @@ import { useAnalytics } from "~/composables/use-analytics"
import { useUiStore } from "~/stores/ui"
import VMetadataValue from "~/components/VMediaInfo/VMetadataValue.vue"
import VSourceProviderTooltip from "~/components/VMediaInfo/VSourceProviderTooltip.vue"
export default defineComponent({
name: "VMetadata",
components: { VMetadataValue },
components: { VSourceProviderTooltip, VMetadataValue },
props: {
metadata: {
type: Array as PropType<Metadata[]>,
Expand All @@ -45,6 +66,15 @@ export default defineComponent({
const uiStore = useUiStore()
const isSm = computed(() => uiStore.isBreakpoint("sm"))
const tooltipId = (datum: Metadata): "source" | "provider" | "" => {
if (
datum.name &&
(datum.name === "source" || datum.name === "provider")
) {
return datum.name
}
return ""
}
const columnCount = computed(() => ({
"--column-count": props.metadata.length,
Expand All @@ -63,6 +93,7 @@ export default defineComponent({
return {
sendVisitSourceLinkEvent,
tooltipId,
isSm,
columnCount,
}
Expand Down
54 changes: 54 additions & 0 deletions frontend/src/components/VMediaInfo/VSourceProviderTooltip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<template>
<dt>
{{ $t(datum.label) }}
<VTooltip placement="top" :described-by="describedBy" class="ms-1">
<template #default>
<p
class="caption-regular rounded-sm bg-dark-charcoal px-2 py-1 text-white"
>
{{ description }}
</p>
</template>
</VTooltip>
</dt>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from "vue"
import { Metadata } from "~/types/media"
import { useI18n } from "~/composables/use-i18n"
import VTooltip from "~/components/VTooltip/VTooltip.vue"
export default defineComponent({
name: "VSourceProviderTooltip",
components: { VTooltip },
props: {
datum: {
type: Object as PropType<Metadata>,
required: true,
},
describedBy: {
type: String,
required: true,
},
},
setup(props) {
const i18n = useI18n()
const description = computed(() => {
if (!props.datum.name) {
return ""
}
return i18n.t(
props.datum.name === "source"
? "mediaDetails.sourceDescription"
: "mediaDetails.providerDescription"
)
})
return {
description,
}
},
})
</script>
17 changes: 0 additions & 17 deletions frontend/src/components/VPopover/VPopover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
ref="triggerContainerRef"
class="flex w-min items-stretch whitespace-nowrap"
@click="onTriggerClick"
@mouseenter="onTriggerMouseEnter"
@focusin="onTriggerMouseEnter"
@mouseleave="onTriggerMouseLeave"
@focusout="onTriggerMouseLeave"
>
<!--
@slot The trigger, should be a button 99.99% of the time. If you need custom event handling on the trigger button, ensure bubbling is not prevented or else the popover will not open
Expand Down Expand Up @@ -180,26 +176,13 @@ export default defineComponent({
emit: emit as SetupContext["emit"],
})
const onTriggerMouseEnter = () => {
if (props.activateOnHover) {
open()
}
}
const onTriggerMouseLeave = () => {
if (props.activateOnHover) {
close()
}
}
return {
open,
close,
visibleRef,
triggerContainerRef,
triggerRef,
onTriggerClick,
onTriggerMouseEnter,
onTriggerMouseLeave,
triggerA11yProps,
}
},
Expand Down
180 changes: 180 additions & 0 deletions frontend/src/components/VTooltip/VTooltip.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<template>
<div>
<!-- re: disabled static element interactions rule https://github.com/WordPress/openverse/issues/2906 -->
<!-- eslint-disable-next-line vuejs-accessibility/click-events-have-key-events, vuejs-accessibility/no-static-element-interactions -->
<div
ref="triggerContainerRef"
class="flex w-min items-stretch whitespace-nowrap"
@keydown.esc="handleEscape"
>
<!--
@slot The trigger, should be a button 99.99% of the time. If you need custom event handling on the trigger button, ensure bubbling is not prevented or else the tooltip will not open
@binding {object} a11yProps
@binding {boolean} visible
-->
<slot name="trigger" :open="open">
<VIconButton
:label="describedBy"
:aria-describedby="describedBy"
variant="bordered-white"
size="disabled"
class="h-4 w-4 hover:!border-tx"
:icon-props="{ name: 'info', size: 4 }"
@click="open"
/>
</slot>
</div>
<div
v-show="visibleRef"
:id="describedBy"
ref="tooltipRef"
role="tooltip"
:class="`z-${zIndex} w-max-content absolute left-0 top-0`"
:style="{ ...style }"
:aria-hidden="!visibleRef"
>
<slot />
</div>
</div>
</template>

<script lang="ts">
import {
defineComponent,
ref,
PropType,
watch,
toRefs,
onBeforeUnmount,
} from "vue"
import { zIndexValidator } from "~/constants/z-indices"
import { useFloatingUi } from "~/composables/use-floating-ui"
import { hasFocusWithin } from "~/utils/reakit-utils/focus"
import VIconButton from "~/components/VIconButton/VIconButton.vue"
import type { Placement, Strategy } from "@floating-ui/dom"
export default defineComponent({
name: "VTooltip",
components: { VIconButton },
props: {
/**
* The placement of the tooltip relative to the trigger. Should be one of the options
* for `placement` passed to floating-ui.
*
* @see https://floating-ui.com/docs/tutorial#placements
*
* @default 'bottom'
*/
placement: {
type: String as PropType<Placement>,
default: "bottom",
},
/**
* The positioning strategy of the tooltip. If your reference element is in a fixed container
* use the fixed strategy; otherwise use the default, absolute strategy.
*
* @see https://floating-ui.com/docs/computeposition#strategy
*
* @default 'absolute'
*/
strategy: {
type: String as PropType<Strategy>,
default: "absolute",
},
/**
* The id of the element labelling the popover content.
* The owning element must have `aria-describedby` set to this value.
*/
describedBy: {
type: String,
required: true,
},
/**
* the z-index to apply to the tooltip content
*/
zIndex: {
type: [Number, String],
default: "popover", // named z-index
validator: zIndexValidator,
},
},
setup(props) {
const visibleRef = ref(false)
const triggerContainerRef = ref<HTMLElement | null>(null)
const closeIfNeeded = () => {
if (triggerRef.value && !hasFocusWithin(triggerRef.value)) {
visibleRef.value = false
}
}
const open = () => {
visibleRef.value = true
}
const listeners = {
mouseover: open,
mouseout: closeIfNeeded,
focus: open,
blur: closeIfNeeded,
}
const setTrigger = (triggerElement: HTMLElement) => {
triggerRef.value = triggerElement
for (const [event, listener] of Object.entries(listeners)) {
triggerElement.addEventListener(event, listener)
}
}
const triggerRef = ref<HTMLElement | null>(null)
watch(triggerContainerRef, (container) => {
if (container) {
setTrigger(container.firstElementChild as HTMLElement)
}
})
onBeforeUnmount(() => {
if (triggerRef.value) {
for (const [event, listener] of Object.entries(listeners)) {
triggerRef.value.removeEventListener(event, listener)
}
}
})
const tooltipRef = ref<HTMLElement | null>(null)
const propsRefs = toRefs(props)
const { style } = useFloatingUi({
floatingElRef: tooltipRef,
floatingPropsRefs: {
placement: propsRefs.placement,
strategy: propsRefs.strategy,
clippable: ref(false),
visible: visibleRef,
triggerElement: triggerRef,
},
})
const handleEscape = () => {
if (visibleRef.value) {
visibleRef.value = false
}
}
return {
visibleRef,
triggerContainerRef,
triggerRef,
tooltipRef,
style,
open,
handleEscape,
}
},
})
</script>
11 changes: 2 additions & 9 deletions frontend/src/composables/use-floating-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,17 @@ import {
Strategy,
} from "@floating-ui/dom"

export type PopoverContentProps = {
export type UseFloatingProps = {
visible: boolean
hide: () => void
hideOnEsc: boolean
hideOnClickOutside: boolean
autoFocusOnShow: boolean
autoFocusOnHide: boolean
triggerElement: HTMLElement | null
placement: Placement
strategy: Strategy
clippable: boolean
trapFocus: boolean
zIndex: number | string
}

type Props = {
floatingElRef: Ref<HTMLElement | null>
floatingPropsRefs: ToRefs<PopoverContentProps>
floatingPropsRefs: ToRefs<UseFloatingProps>
}

// A constant offset to ensure there's a gap between
Expand Down
Loading

0 comments on commit 5475c78

Please sign in to comment.