Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 52d25bc

Browse files
authored
Merge pull request #213 from WordPress/add/download-button
Add DownloadButton component
2 parents 8ebe51f + 1cff523 commit 52d25bc

File tree

9 files changed

+390
-18
lines changed

9 files changed

+390
-18
lines changed

package-lock.json

Lines changed: 91 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@
4141
"@nuxtjs/sitemap": "^2.4.0",
4242
"@nuxtjs/svg": "^0.1.12",
4343
"axios": "^0.21.1",
44+
"axios-mock-adapter": "^1.20.0",
4445
"babel-core": "^7.0.0-bridge.0",
4546
"babel-eslint": "^10.1.0",
4647
"babel-jest": "^26.6.3",
4748
"build-url": "^6.0.1",
4849
"case": "^1.6.3",
4950
"clipboard": "^2.0.8",
5051
"deepmerge": "^4.2.2",
52+
"filesize": "^8.0.2",
5153
"focus-trap": "^5.1.0",
5254
"focus-trap-vue": "0.0.6",
5355
"js-cookie": "^2.2.1",

src/components/DownloadButton.vue

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<template>
2+
<DropdownButton
3+
v-if="filesizes"
4+
:dropdown-aria-label="$t('download-button.aria.dropdown-label')"
5+
>
6+
<template #default="{ buttonProps }">
7+
<a
8+
v-bind="buttonProps"
9+
class="whitespace-nowrap"
10+
:href="selectedFormat.download_url"
11+
download=""
12+
>
13+
<span>{{ $t('download-button.download') }}</span>
14+
<span class="ml-4 selected-format">
15+
{{ selectedFormat.extension_name }}
16+
</span>
17+
<span class="ml-1 font-normal">{{
18+
getFormatSize(selectedFormat.extension_name)
19+
}}</span>
20+
</a>
21+
</template>
22+
23+
<template
24+
#items="{
25+
activeItemClass,
26+
itemClass,
27+
itemA11yProps,
28+
toggleOpen,
29+
onItemKeydown,
30+
}"
31+
>
32+
<ul>
33+
<li v-for="format in formats" :key="format.name">
34+
<button
35+
class="flex justify-between w-full"
36+
:class="[
37+
itemClass,
38+
selectedFormat.extension_name === format.extension_name
39+
? activeItemClass
40+
: '',
41+
]"
42+
type="button"
43+
v-bind="itemA11yProps"
44+
@click="
45+
setSelectedFormat(format)
46+
toggleOpen()
47+
"
48+
@keydown="onItemKeydown($event)"
49+
>
50+
<span class="font-bold mr-2">{{ format.extension_name }}</span>
51+
<span>{{ getFormatSize(format.extension_name) }}</span>
52+
</button>
53+
</li>
54+
</ul>
55+
</template>
56+
</DropdownButton>
57+
</template>
58+
59+
<script>
60+
import filesize from 'filesize'
61+
import axios from 'axios'
62+
import local from '~/utils/local'
63+
import DropdownButton from '~/components/DropdownButton'
64+
65+
const LS_DOWNLOAD_FORMAT_EXTENSION_KEY = 'openverse:download-format-extension'
66+
67+
export default {
68+
name: 'DownloadButton',
69+
components: { DropdownButton },
70+
props: {
71+
formats: {
72+
type: Array,
73+
required: true,
74+
validator: (formats) => {
75+
const properties = ['extension_name', 'download_url']
76+
return formats.every((format) => properties.every((p) => p in format))
77+
},
78+
},
79+
},
80+
data() {
81+
const savedFormatExtension =
82+
local.get(LS_DOWNLOAD_FORMAT_EXTENSION_KEY) ?? null
83+
let format = this.formats[0]
84+
if (savedFormatExtension) {
85+
const found = this.formats.find(
86+
(format) => format.extension_name === savedFormatExtension
87+
)
88+
if (found) {
89+
format = found
90+
}
91+
}
92+
93+
return { selectedFormat: format, filesizes: null }
94+
},
95+
async fetch() {
96+
const extensionsToFilesizes = await Promise.all(
97+
this.formats.map(async (format) => {
98+
try {
99+
const response = await axios.head(format.download_url)
100+
return [format.extension_name, response.headers['content-length']]
101+
} catch (e) {
102+
return [format.extension_name, undefined]
103+
}
104+
})
105+
)
106+
107+
this.filesizes = extensionsToFilesizes.reduce(
108+
(acc, [extensionName, filesize]) => ({
109+
...acc,
110+
[extensionName]: filesize,
111+
}),
112+
{}
113+
)
114+
},
115+
methods: {
116+
getFormatSize(extensionName) {
117+
const size = this.filesizes?.[extensionName] ?? undefined
118+
if (typeof size === 'undefined') return ''
119+
120+
return filesize(size, { locale: this.$i18n.locale })
121+
},
122+
setSelectedFormat(format) {
123+
local.set(LS_DOWNLOAD_FORMAT_EXTENSION_KEY, format.extension_name)
124+
this.selectedFormat = format
125+
},
126+
},
127+
}
128+
</script>

src/components/DropdownButton.vue

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
class="dropdown-button ml-1 rounded-r-sm rounded-l-none"
1515
:class="{ 'dropdown-button-active': isOpen }"
1616
aria-haspopup="menu"
17-
:aria-label="$t('dropdown-button.aria.arrow-label')"
17+
:aria-label="safeDropdownAriaLabel"
1818
:aria-expanded="isOpen"
1919
@click="toggleOpen"
2020
@keydown.space.prevent="toggleOpen"
2121
>
22-
<svg class="h-2 w-4">
22+
<svg class="h-2 w-4 pointer-events-none">
2323
<use :href="`${icons.caretDown}#icon`" />
2424
</svg>
2525
</button>
@@ -50,12 +50,22 @@ import caretDown from '~/assets/icons/caret-down.svg'
5050
5151
const DropdownButton = {
5252
name: 'DropdownButton',
53-
data: () => ({
54-
isOpen: false,
55-
icons: {
56-
caretDown,
53+
props: {
54+
dropdownAriaLabel: {
55+
type: String,
56+
required: false,
5757
},
58-
}),
58+
},
59+
data() {
60+
return {
61+
isOpen: false,
62+
icons: {
63+
caretDown,
64+
},
65+
safeDropdownAriaLabel:
66+
this.dropdownAriaLabel || this.$t('dropdown-button.aria.arrow-label'),
67+
}
68+
},
5969
mounted() {
6070
document.addEventListener('click', this.onClickout)
6171
},
@@ -136,7 +146,7 @@ export default DropdownButton
136146

137147
<style lang="css" scoped>
138148
.dropdown-button {
139-
@apply flex items-center justify-center bg-pink text-white font-bold p-2 px-4 transition-shadow duration-100 ease-linear disabled:opacity-70 focus:outline-none focus-visible:ring focus-visible:ring-offset-2 focus-visible:ring-pink hover:bg-dark-pink;
149+
@apply flex items-center justify-center bg-pink text-white font-bold p-2 px-4 transition-shadow duration-100 ease-linear disabled:opacity-70 focus:outline-none focus-visible:ring focus-visible:ring-offset-2 focus-visible:ring-pink hover:bg-dark-pink no-underline appearance-none;
140150
}
141151
142152
.dropdown-button-active {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import DownloadButton from '~/components/DownloadButton'
2+
3+
export default {
4+
title: 'Components/DownloadButton',
5+
component: DownloadButton,
6+
}
7+
8+
export const Default = () => ({
9+
template: `<DownloadButton :formats="formats" />`,
10+
components: { DownloadButton },
11+
data: () => ({
12+
formats: [
13+
{
14+
extension_name: 'MP3 98kbs',
15+
download_url: 'https://mp3d.jamendo.com/download/track/1532771/mp31/',
16+
},
17+
{
18+
extension_name: 'MP3 VBR',
19+
download_url: 'https://mp3d.jamendo.com/download/track/1532771/mp32/',
20+
},
21+
{
22+
extension_name: 'FLAC',
23+
download_url: 'https://mp3d.jamendo.com/download/track/1532771/flac/',
24+
},
25+
{
26+
extension_name: 'OGG',
27+
download_url: 'https://mp3d.jamendo.com/download/track/1532771/ogg/',
28+
},
29+
],
30+
}),
31+
})

0 commit comments

Comments
 (0)