Merge pull request #440 from Human-Connection/2019/kw15/design_differences_post

2019/kw15/design_differences_post
This commit is contained in:
Ulf Gebhardt 2019-04-12 14:14:24 +02:00 committed by GitHub
commit 39c6c97eda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 322 additions and 73 deletions

View File

@ -10,7 +10,7 @@
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js -e js,graphql",
"lint": "eslint src --config .eslintrc.js",
"test": "run-s test:jest test:cucumber",
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 yarn run dev 2> /dev/null",
"test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 yarn run dev",
"test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions,activityPub yarn run dev",
"test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand",
"test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/",
@ -94,4 +94,4 @@
"nodemon": "~1.18.11",
"supertest": "~4.0.2"
}
}
}

View File

@ -228,7 +228,7 @@ Then('I get redirected to {string}', route => {
})
Then('the post was saved successfully', () => {
cy.get('.ds-card-header > .ds-heading').should('contain', lastPost.title)
cy.get('.ds-card-content > .ds-heading').should('contain', lastPost.title)
cy.get('.content').should('contain', lastPost.content)
})

View File

@ -25,4 +25,4 @@
"neo4j-driver": "^1.7.3",
"npm-run-all": "^4.1.5"
}
}
}

View File

@ -0,0 +1,7 @@
### Example
Category "IT, Internet & Data Privacy" with icon "mouse-cursor"
```
<hc-category icon="mouse-pointer" name="IT, Internet & Data Privacy" />
```

View File

@ -0,0 +1,35 @@
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Styleguide from '@human-connection/styleguide'
import Category from './index'
const localVue = createLocalVue()
localVue.use(Styleguide)
describe('Category', () => {
let icon
let name
let Wrapper = () => {
return shallowMount(Category, {
localVue,
propsData: {
icon,
name
}
})
}
describe('given Strings for Icon and Name', () => {
beforeEach(() => {
icon = 'mouse-cursor'
name = 'Peter'
})
it('shows Name', () => {
expect(Wrapper().text()).toContain('Peter')
})
it('shows Icon Svg', () => {
expect(Wrapper().contains('svg'))
})
})
})

View File

@ -0,0 +1,19 @@
<template>
<ds-tag>
<ds-icon
size="large"
:name="icon"
/>
{{ name }}
</ds-tag>
</template>
<script>
export default {
name: 'HcCategory',
props: {
icon: { type: String, required: true },
name: { type: String, required: true }
}
}
</script>

View File

@ -4,16 +4,15 @@
style="padding-left: 40px; font-weight: bold;"
color="soft"
>
<ds-icon name="ban" /> {{ this.$t('comment.content.unavailable-placeholder') }}
<ds-icon name="ban" />
{{ this.$t('comment.content.unavailable-placeholder') }}
</ds-text>
</div>
<div
v-else
:class="{'comment': true, 'disabled-content': (comment.deleted || comment.disabled)}"
>
<ds-space
margin-bottom="x-small"
>
<ds-space margin-bottom="x-small">
<hc-user :user="author" />
</ds-space>
<no-ssr>
@ -32,13 +31,13 @@
style="padding-left: 40px;"
v-html="comment.contentExcerpt"
/>
<!-- eslint-enable vue/no-v-html -->
<!-- eslint-enable vue/no-v-html -->
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import HcUser from '~/components/User.vue'
import HcUser from '~/components/User'
import ContentMenu from '~/components/ContentMenu'
export default {

View File

@ -72,7 +72,7 @@
</template>
<script>
import HcUser from '~/components/User.vue'
import HcUser from '~/components/User'
import ContentMenu from '~/components/ContentMenu'
import { randomBytes } from 'crypto'

View File

@ -0,0 +1,7 @@
### Example
Relative time from 08.03.2017
```
<hc-relative-date-time dateTime="03.08.2017" />
```

View File

@ -0,0 +1,79 @@
import { shallowMount, createLocalVue } from '@vue/test-utils'
import RelativeDateTime from './index'
const localVue = createLocalVue()
describe('RelativeDateTime', () => {
let wrapper
let mocks
let locale
let dateTime
beforeEach(() => {
mocks = {
$i18n: {
locale: () => locale
}
}
})
let Wrapper = () => {
return shallowMount(RelativeDateTime, {
mocks,
localVue,
propsData: {
dateTime
}
})
}
describe('given a String as dateTime', () => {
beforeEach(() => {
dateTime = '08.03.2017'
})
it('translates', () => {
expect(Wrapper().text()).toContain('08/03/2017')
})
})
describe('given a Date object as dateTime', () => {
beforeEach(() => {
dateTime = new Date()
})
it('renders', () => {
expect(Wrapper().is('span')).toBe(true)
})
describe("locale == 'en'", () => {
beforeEach(() => {
locale = 'en'
})
it('translates', () => {
expect(Wrapper().text()).toContain('today at')
})
})
describe("locale == 'gibberish'", () => {
beforeEach(() => {
locale = 'gibberish'
})
it('translates', () => {
expect(Wrapper().text()).toContain('today at')
})
})
describe("locale == 'de'", () => {
beforeEach(() => {
locale = 'de'
})
it('translates', () => {
expect(Wrapper().text()).toContain('heute um')
})
})
})
})

View File

@ -0,0 +1,33 @@
<template>
<span>{{ relativeDateTime }}</span>
</template>
<script>
import formatRelative from 'date-fns/formatRelative'
import { enUS, de, nl, fr, pt, es /*, pl*/ } from 'date-fns/locale'
const locales = {
en: enUS,
de,
nl,
fr,
es,
pt
// pl
}
export default {
name: 'HcRelativeDateTime',
props: {
dateTime: {
type: [Date, String],
required: true
}
},
computed: {
relativeDateTime() {
let locale = locales[this.$i18n.locale() || 'en']
return formatRelative(new Date(this.dateTime), new Date(), { locale })
}
}
}
</script>

View File

@ -0,0 +1,7 @@
### Example
Tag "Liebe"
```
<hc-tag name="Liebe" />
```

View File

@ -0,0 +1,29 @@
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Styleguide from '@human-connection/styleguide'
import Tag from './index'
const localVue = createLocalVue()
localVue.use(Styleguide)
describe('Tag', () => {
let name
let Wrapper = () => {
return shallowMount(Tag, {
localVue,
propsData: {
name
}
})
}
describe('given a String for Name', () => {
beforeEach(() => {
name = 'Liebe'
})
it('shows Name', () => {
expect(Wrapper().text()).toContain('Liebe')
})
})
})

View File

@ -0,0 +1,15 @@
<template>
<ds-tag>
<ds-icon name="tag" />
{{ name }}
</ds-tag>
</template>
<script>
export default {
name: 'HcTag',
props: {
name: { type: String, required: true }
}
}
</script>

View File

@ -1,5 +1,5 @@
import { config, mount, createLocalVue, RouterLinkStub } from '@vue/test-utils'
import User from './User.vue'
import User from './index'
import Vue from 'vue'
import Vuex from 'vuex'
import VTooltip from 'v-tooltip'
@ -15,7 +15,7 @@ localVue.use(Styleguide)
localVue.filter('truncate', filter)
describe('User.vue', () => {
describe('User', () => {
let wrapper
let Wrapper
let propsData

View File

@ -1,6 +1,8 @@
<template>
<div v-if="!user || ((user.disabled || user.deleted) && !isModerator)">
<div style="display: inline-block; float: left; margin-right: 4px; height: 100%; vertical-align: middle;">
<div
style="display: inline-block; float: left; margin-right: 4px; height: 100%; vertical-align: middle;"
>
<ds-avatar
style="display: inline-block; vertical-align: middle;"
size="32px"
@ -10,9 +12,7 @@
<b
class="username"
style="vertical-align: middle;"
>
Anonymus
</b>
>Anonymus</b>
</div>
</div>
<dropdown
@ -33,7 +33,9 @@
@mouseover="openMenu(true)"
@mouseleave="closeMenu(true)"
>
<div style="display: inline-block; float: left; margin-right: 4px; height: 100%; vertical-align: middle;">
<div
style="display: inline-block; float: left; margin-right: 4px; height: 100%; vertical-align: middle;"
>
<ds-avatar
:image="user.avatar"
:name="user.name"
@ -45,16 +47,28 @@
<b
class="username"
style="vertical-align: middle;"
>{{ user.name | truncate(trunc, 18) }}</b>
</div>
<!-- Time -->
<div
v-if="dateTime"
style="display: inline;"
>
<ds-text
align="right"
size="small"
color="soft"
>
{{ user.name | truncate(trunc, 18) }}
</b>
<ds-icon name="clock" />
<no-ssr>
<hc-relative-date-time :date-time="dateTime" />
</no-ssr>
</ds-text>
</div>
</div>
</nuxt-link>
</template>
<template
slot="popover"
>
<template slot="popover">
<div style="min-width: 250px">
<hc-badges
v-if="user.badges && user.badges.length"
@ -68,11 +82,10 @@
style="margin-top: 5px"
bold
>
<ds-icon name="map-marker" /> {{ user.location.name }}
<ds-icon name="map-marker" />
{{ user.location.name }}
</ds-text>
<ds-flex
style="margin-top: -10px"
>
<ds-flex style="margin-top: -10px">
<ds-flex-item class="ds-tab-nav-item">
<ds-space margin="small">
<ds-number
@ -125,21 +138,25 @@
</template>
<script>
import { mapGetters } from 'vuex'
import HcRelativeDateTime from '~/components/RelativeDateTime'
import HcFollowButton from '~/components/FollowButton.vue'
import HcBadges from '~/components/Badges.vue'
import Dropdown from '~/components/Dropdown'
import { mapGetters } from 'vuex'
export default {
name: 'HcUser',
components: {
HcRelativeDateTime,
HcFollowButton,
HcBadges,
Dropdown
},
props: {
user: { type: Object, default: null },
trunc: { type: Number, default: null }
trunc: { type: Number, default: null },
dateTime: { type: [Date, String], default: null }
},
computed: {
...mapGetters({

View File

@ -49,6 +49,7 @@
"linkify-it": "~2.1.0",
"nuxt": "~2.6.1",
"nuxt-env": "~0.1.0",
"stack-utils": "^1.0.2",
"string-hash": "^1.1.3",
"tiptap": "^1.14.0",
"tiptap-extensions": "^1.14.0",

View File

@ -6,10 +6,13 @@
<ds-card
v-if="post && ready"
:image="post.image"
:header="post.title"
:class="{'post-card': true, 'disabled-content': post.disabled}"
>
<hc-user :user="post.author" />
<ds-space margin-bottom="small" />
<hc-user
:user="post.author"
:date-time="post.createdAt"
/>
<no-ssr>
<content-menu
placement="bottom-end"
@ -19,6 +22,13 @@
/>
</no-ssr>
<ds-space margin-bottom="small" />
<ds-heading
tag="h3"
no-margin
>
{{ post.title }}
</ds-heading>
<ds-space margin-bottom="small" />
<!-- Content -->
<!-- eslint-disable vue/no-v-html -->
<!-- TODO: replace editor content with tiptap render view -->
@ -26,19 +36,33 @@
class="content hc-editor-content"
v-html="post.content"
/>
<ds-space>
<ds-text
v-if="post.createdAt"
align="right"
size="small"
color="soft"
>
{{ post.createdAt | dateTime('dd. MMMM yyyy HH:mm') }}
</ds-text>
</ds-space>
<!-- eslint-enable vue/no-v-html -->
<!-- Shout Button -->
<ds-space margin="xx-large" />
<!-- Categories -->
<div class="categories">
<ds-space margin="xx-small" />
<hc-category
v-for="category in post.categories"
:key="category.id"
v-tooltip="{content: category.name, placement: 'top-start', delay: { show: 300 }}"
:icon="category.icon"
:name="category.name"
/>
</div>
<ds-space margin-bottom="small" />
<!-- Tags -->
<div
v-if="post.tags && post.tags.length"
class="tags"
>
<ds-space margin="xx-small" />
<hc-tag
v-for="tag in post.tags"
:key="tag.id"
:name="tag.name"
/>
</div>
<!-- Shout Button -->
<hc-shout-button
v-if="post.author"
:disabled="isAuthor(post.author.id)"
@ -55,14 +79,6 @@
size="large"
/>&nbsp;
<ds-space margin-bottom="small" />
<!--<div class="tags">
<ds-icon name="compass" /> <ds-tag
v-for="category in post.categories"
:key="category.id"
>
{{ category.name }}
</ds-tag>
</div>-->
<!-- Tags -->
<template v-if="post.tags && post.tags.length">
<ds-space margin="xx-small" />
@ -115,8 +131,11 @@
<script>
import gql from 'graphql-tag'
import HcCategory from '~/components/Category'
import HcTag from '~/components/Tag'
import ContentMenu from '~/components/ContentMenu'
import HcUser from '~/components/User.vue'
import HcUser from '~/components/User'
import HcShoutButton from '~/components/ShoutButton.vue'
import HcEmpty from '~/components/Empty.vue'
import Comment from '~/components/Comment.vue'
@ -127,6 +146,8 @@ export default {
mode: 'out-in'
},
components: {
HcTag,
HcCategory,
HcUser,
HcShoutButton,
HcEmpty,

View File

@ -291,7 +291,7 @@
<script>
import uniqBy from 'lodash/uniqBy'
import User from '~/components/User.vue'
import User from '~/components/User'
import HcPostCard from '~/components/PostCard.vue'
import HcFollowButton from '~/components/FollowButton.vue'
import HcCountTo from '~/components/CountTo.vue'

View File

@ -34,12 +34,6 @@ export default ({ app }) => {
locale: getLocalizedFormat()
})
},
relativeDateTime: value => {
if (!value) return ''
return formatRelative(new Date(value), new Date(), {
locale: getLocalizedFormat()
})
},
number: (
value,
precision = 2,

View File

@ -1381,11 +1381,6 @@
"@types/express-serve-static-core" "*"
"@types/mime" "*"
"@types/stack-utils@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
"@types/strip-bom@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2"
@ -6164,15 +6159,6 @@ jest-message-util@^24.7.1:
version "24.7.1"
resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.7.1.tgz#f1dc3a6c195647096a99d0f1dadbc447ae547018"
integrity sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg==
dependencies:
"@babel/code-frame" "^7.0.0"
"@jest/test-result" "^24.7.1"
"@jest/types" "^24.7.0"
"@types/stack-utils" "^1.0.1"
chalk "^2.0.1"
micromatch "^3.1.10"
slash "^2.0.0"
stack-utils "^1.0.1"
jest-mock@^24.7.0:
version "24.7.0"
@ -9879,7 +9865,7 @@ stack-trace@0.0.10:
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=
stack-utils@^1.0.1:
stack-utils@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8"
integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==