Improve WebDev level image gallery

Improve image gallery

Add How to Copy/Paste section

Fix modal close button

Add specs for image gallery

Fix up i18n

Fix render resetting scroll

Address code review feedback

Ensure afterRender is called
This commit is contained in:
Phoenix Eliot 2016-08-08 17:13:17 -07:00
parent f96c3ab00e
commit 73bbe598da
8 changed files with 293 additions and 22 deletions

View file

@ -49,6 +49,10 @@ toHex = (n) ->
h = '0'+h if h.length is 1
h
module.exports.pathToUrl = (path) ->
base = location.protocol + '//' + location.hostname + (location.port && ":" + location.port)
base + path
module.exports.i18n = (say, target, language=me.get('preferredLanguage', true), fallback='en') ->
generalResult = null
fallBackResult = null
@ -184,6 +188,10 @@ if document?.createElement
return
)(document)
# So that we can stub out userAgent in tests
module.exports.userAgent = ->
window.navigator.userAgent
module.exports.getQueryVariable = getQueryVariable = (param, defaultValue) ->
query = document.location.search.substring 1
pairs = (pair.split('=') for pair in query.split '&')

View file

@ -1527,7 +1527,18 @@
web_dev:
image_gallery_title: "Image Gallery"
image_gallery_description: "Copy these images into your webpage, or find your own image URLs online."
select_an_image: "Select an image you want to use"
scroll_down_for_more_images: "(Scroll down for more images)"
copy_the_url: "Copy the URL below"
copy_the_url_description: "Useful if you want to replace an existing image."
copy_the_img_tag: "Copy the <img> tag"
copy_the_img_tag_description: "Useful if you want to insert a new image."
copy_url: "Copy URL"
copy_img: "Copy <img>"
how_to_copy_paste: "How to Copy/Paste"
copy: "Copy"
paste: "Paste"
back_to_editing: "Back to Editing"
classes:
archmage_title: "Archmage"

View file

@ -97,7 +97,7 @@
justify-content: space-between
.btn
// Undo .style-flat's .btn ~ .btn margin
// Undo .style-flat's .btn + .btn margin
margin: 0
&.just-one

View file

@ -1,11 +1,117 @@
@import "app/styles/mixins"
@import "app/styles/style-flat-variables"
#image-gallery-modal
h3
font-size: 20px
.subtitle
font-size: 14px
line-height: 14px
.modal-dialog
width: 800px
li
font-size: 12px
width: 850px
height: 550px
max-height: 100vh
.modal-footer
display: none
.modal-header
padding: 0
min-height: 0
.modal-body-content
height: 485px
.no-select
@include user-select(none)
.image-list
overflow: -moz-scrollbars-vertical
overflow-y: scroll
margin: 0
// Force scrollbar visible
&::-webkit-scrollbar
// -webkit-appearance: none
border: thin solid gray
width: 14px
&::-webkit-scrollbar-thumb
background-color: rgba(0,0,0,.5)
.flex-col
display: flex
flex-direction: column
height: 100%
.image-list
height: 440px
max-height: 100vh
padding: 0
display: flex
flex-wrap: wrap
.image-list-item
img
width: 72px
height: 72px
margin: 16px
list-style-type: none
background-color: #f8f8f8
box-shadow: 0 0 0 1px gray
&.selected
box-shadow: 0 0 0 6px $gold
//
.copy-row
display: flex
align-items: center
.copy-textarea-col
flex-grow: 1
textarea
width: 100%
height: 55px
.copy-button-col
padding-left: 10px
.copyable
font-size: 10px
line-height: 12px
.how-to-copy-paste
font-size: 13px
line-height: 16px
font-style: italic
color: gray
.close-button
flex-grow: 1
align-self: flex-end
display: flex
align-items: flex-end
// Fancy text inside horizontal rules
.hr-text
position: relative
hr
width: 50%
padding: 0
border: none
border-top: thin solid #444
color: #444
span
position: absolute
left: 50%
top: 0.45em
transform: translateX(-50%)
padding: 0 0.75em
font-weight: bold
font-size: 10pt
background: white

View file

@ -212,7 +212,7 @@ body[lang='ru'], body[lang='uk'], body[lang='bg'], body[lang^='mk'], body[lang='
.disabled
opacity: 50%
.btn ~ .btn
.btn + .btn
margin-left: 12px
.btn-primary, .btn-navy

View file

@ -1,23 +1,67 @@
extends /templates/core/modal-base-flat
block modal-header-content
h3(data-i18n="web_dev.image_gallery_title")
span(data-i18n="web_dev.image_gallery_description")
block modal-body-content
dl.dl-horizontal
for image in view.images
dt
img(src=image.portraitURL)
dd
ul.list-unstyled
li
span.no-select= 'URL: '
kbd= image.portraitURL
.row.modal-body-content
div.image-list-container.col-sm-7
h3
='1. '
span(data-i18n="web_dev.select_an_image")
ul.image-list
for image in view.images
- var selectedState = state.get('selectedUrl') === image.portraitURL ? 'selected' : ''
li.image-list-item.render(data-portrait-url=image.portraitURL, class=selectedState, selected=selectedState)
img(src=image.portraitURL)
.small.text-center
span(data-i18n="web_dev.scroll_down_for_more_images")
div.col-sm-5.flex-col.render
h3
='2. '
span(data-i18n="web_dev.copy_the_url")
.text-h3.subtitle(data-i18n="web_dev.copy_the_url_description")
.copy-row.m-t-1
.copy-textarea-col
textarea.image-url.copyable
if view.state.get('selectedUrl')
= utils.pathToUrl(view.state.get('selectedUrl'))
.copy-button-col
button.btn.btn-forest.copy-url-button
span(data-i18n="web_dev.copy_url")
.hr-text.m-t-1
hr
span(data-i18n="general.or")
h3(data-i18n="web_dev.copy_the_img_tag")
.text-h3.subtitle(data-i18n="web_dev.copy_the_img_tag_description")
.copy-row.m-t-1
.copy-textarea-col
textarea.image-tag.copyable
if view.state.get('selectedUrl')
= '<img src="' + utils.pathToUrl(view.state.get('selectedUrl')) + '"/>'
.copy-button-col
button.btn.btn-forest.copy-tag-button
span(data-i18n="web_dev.copy_img")
.how-to-copy-paste.m-t-3
div.m-b-1.text-center
span(data-i18n="web_dev.how_to_copy_paste")
.windows-only
span(data-i18n="web_dev.copy")
| : ControlC
br
li
span.no-select= '<img>: '
kbd= '<img src="' + image.portraitURL + '">'
span(data-i18n="web_dev.paste")
| : ControlV
.mac-only.hidden
span(data-i18n="web_dev.copy")
| : Command ⌘C
br
span(data-i18n="web_dev.paste")
| : Command ⌘V
.close-button
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="web_dev.back_to_editing").btn.btn-lg.btn-primary
block modal-footer-content
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="modal.close").btn.btn-primary Close

View file

@ -1,8 +1,42 @@
ModalView = require 'views/core/ModalView'
State = require 'models/State'
utils = require 'core/utils'
module.exports = class ImageGalleryModal extends ModalView
id: 'image-gallery-modal'
template: require 'templates/play/level/modal/image-gallery-modal'
events:
'click .image-list-item': 'onClickImageListItem'
'click .copy-url-button': 'onClickCopyUrlButton'
'click .copy-tag-button': 'onClickCopyTagButton'
getRenderData: ->
_.merge super(arguments...), { utils }
initialize: ->
@state = new State()
@listenTo @state, 'all', =>
@renderSelectors('.render')
@afterRender()
afterRender: ->
if utils.userAgent().indexOf("Mac") > -1
@$('.windows-only').addClass('hidden')
@$('.mac-only').removeClass('hidden')
onClickImageListItem: (e) ->
selectedUrl = $(e.currentTarget).data('portrait-url')
@state.set { selectedUrl }
onClickCopyUrlButton: (e) ->
$('.image-url').select()
@tryCopy()
onClickCopyTagButton: (e) ->
$('.image-tag').select()
@tryCopy()
# Top most useful Thang portraits
images: [
{slug: 'archer-f', name: 'Archer F', original: '529ab1a24b67a988ad000002', portraitURL: '/file/db/thang.type/529ab1a24b67a988ad000002/portrait.png', kind: 'Unit'}

View file

@ -0,0 +1,68 @@
Course = require 'models/Course'
Level = require 'models/Level'
LevelSession = require 'models/LevelSession'
ImageGalleryModal = require 'views/play/level/modal/ImageGalleryModal'
ProgressView = require 'views/play/level/modal/ProgressView'
factories = require 'test/app/factories'
utils = require 'core/utils'
describe 'ImageGalleryModal', ->
modal = null
beforeEach (done) ->
modal = new ImageGalleryModal()
modal.render()
_.defer done
it '(demo)', ->
jasmine.demoModal(modal)
it 'shows a list of images', ->
expect(modal.$('img').length).toBeGreaterThan(16)
describe 'clicking an image', ->
beforeEach (done) ->
@clickedImage = modal.$('li:nth-child(5)').click()
@clickedImagePath = @clickedImage.data('portrait-url')
@clickedImageUrl = utils.pathToUrl(@clickedImagePath)
@clickedImageTag = '<img src="' + @clickedImageUrl + '"/>'
_.defer done
it 'highlights the chosen image', ->
expect(modal.$('li:nth-child(5)').hasClass('selected')).toBe(true)
it 'displays the URL/image tags in the Copy section', ->
expect(modal.$('.image-url').text()).toBe(@clickedImageUrl)
expect(modal.$('.image-tag').text()).toBe(@clickedImageTag)
describe "How to Copy/Paste section", ->
userAgents = {
windows: 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko'
mac: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
}
it 'Shows Windows shortcuts to Windows users', (done) ->
spyOn(utils, 'userAgent').and.callFake ->
userAgents.windows
modal.render()
# This test is a little fragile  Only works if the text node is an immediate child to .windows-only
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).toMatch(/Control|Ctrl/i)
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).not.toMatch(/Command|Cmd/i)
@clickedImage = modal.$('li:nth-child(5)').click()
_.defer ->
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).toMatch(/Control|Ctrl/i)
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).not.toMatch(/Command|Cmd/i)
done()
it 'Shows Mac shortcuts to Mac users', (done) ->
spyOn(utils, 'userAgent').and.callFake ->
userAgents.mac
modal.render()
# This test is a little fragile  Only works if the text node is an immediate child to .mac-only
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).toMatch(/Command|Cmd/i)
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).not.toMatch(/Control|Ctrl/i)
@clickedImage = modal.$('li:nth-child(5)').click()
_.defer ->
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).toMatch(/Command|Cmd/i)
expect(modal.$('.how-to-copy-paste :not(.hidden)').text()).not.toMatch(/Control|Ctrl/i)
done()