mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
Merge pull request #2700 from Human-Connection/2675-migrate-avatar-component-2
refactor(styleguide): Migrate Avatar component to monorepo
This commit is contained in:
commit
901245b718
@ -24,10 +24,10 @@ Then("my comment should be successfully created", () => {
|
|||||||
Then("I should see my comment", () => {
|
Then("I should see my comment", () => {
|
||||||
cy.get("div.comment p")
|
cy.get("div.comment p")
|
||||||
.should("contain", "Human Connection rocks")
|
.should("contain", "Human Connection rocks")
|
||||||
.get(".ds-avatar img")
|
.get(".user-avatar img")
|
||||||
.should("have.attr", "src")
|
.should("have.attr", "src")
|
||||||
.and("contain", narratorAvatar)
|
.and("contain", narratorAvatar)
|
||||||
.get("div p.ds-text span")
|
.get(".user-teaser > .info > .text")
|
||||||
.should("contain", "today at");
|
.should("contain", "today at");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -32,5 +32,5 @@ Then("I cannot upload a picture", () => {
|
|||||||
cy.get(".ds-card-content")
|
cy.get(".ds-card-content")
|
||||||
.children()
|
.children()
|
||||||
.should("not.have.id", "customdropzone")
|
.should("not.have.id", "customdropzone")
|
||||||
.should("have.class", "ds-avatar");
|
.should("have.class", "user-avatar");
|
||||||
});
|
});
|
||||||
|
|||||||
@ -63,7 +63,7 @@ When('I click on "Report User" from the content menu in the user info box', () =
|
|||||||
})
|
})
|
||||||
|
|
||||||
When('I click on the author', () => {
|
When('I click on the author', () => {
|
||||||
cy.get('.username')
|
cy.get('.user-teaser')
|
||||||
.click()
|
.click()
|
||||||
.url().should('include', '/profile/')
|
.url().should('include', '/profile/')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -87,7 +87,7 @@ Then(
|
|||||||
);
|
);
|
||||||
|
|
||||||
Then("I select a user entry", () => {
|
Then("I select a user entry", () => {
|
||||||
cy.get(".searchable-input .userinfo")
|
cy.get(".searchable-input .user-teaser")
|
||||||
.first()
|
.first()
|
||||||
.trigger("click");
|
.trigger("click");
|
||||||
})
|
})
|
||||||
|
|||||||
@ -463,7 +463,7 @@ When(
|
|||||||
);
|
);
|
||||||
|
|
||||||
When("I navigate to my {string} settings page", settingsPage => {
|
When("I navigate to my {string} settings page", settingsPage => {
|
||||||
cy.get(".avatar-menu").click();
|
cy.get(".avatar-menu-trigger").click();
|
||||||
cy.get(".avatar-menu-popover")
|
cy.get(".avatar-menu-popover")
|
||||||
.find("a[href]")
|
.find("a[href]")
|
||||||
.contains("Settings")
|
.contains("Settings")
|
||||||
|
|||||||
@ -254,8 +254,7 @@ $size-width-paginate: 100px;
|
|||||||
|
|
||||||
$size-avatar-small: 34px;
|
$size-avatar-small: 34px;
|
||||||
$size-avatar-base: 44px;
|
$size-avatar-base: 44px;
|
||||||
$size-avatar-large: 64px;
|
$size-avatar-large: 114px;
|
||||||
$size-avatar-x-large: 114px;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @tokens Size Buttons
|
* @tokens Size Buttons
|
||||||
|
|||||||
@ -1,68 +0,0 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import Avatar from './Avatar.vue'
|
|
||||||
|
|
||||||
const localVue = global.localVue
|
|
||||||
|
|
||||||
describe('Avatar.vue', () => {
|
|
||||||
let propsData = {}
|
|
||||||
|
|
||||||
const Wrapper = () => {
|
|
||||||
return mount(Avatar, { propsData, localVue })
|
|
||||||
}
|
|
||||||
|
|
||||||
it('renders no image', () => {
|
|
||||||
expect(
|
|
||||||
Wrapper()
|
|
||||||
.find('img')
|
|
||||||
.exists(),
|
|
||||||
).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
// this is testing the style guide
|
|
||||||
it('renders an icon', () => {
|
|
||||||
expect(
|
|
||||||
Wrapper()
|
|
||||||
.find('.ds-icon')
|
|
||||||
.exists(),
|
|
||||||
).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('given a user', () => {
|
|
||||||
describe('with a relative avatar url', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
propsData = {
|
|
||||||
user: {
|
|
||||||
avatar: '/avatar.jpg',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it('adds a prefix to load the image from the uploads service', () => {
|
|
||||||
expect(
|
|
||||||
Wrapper()
|
|
||||||
.find('img')
|
|
||||||
.attributes('src'),
|
|
||||||
).toBe('/api/avatar.jpg')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with an absolute avatar url', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
propsData = {
|
|
||||||
user: {
|
|
||||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/sawalazar/128.jpg',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
it('keeps the avatar URL as is', () => {
|
|
||||||
// e.g. our seeds have absolute image URLs
|
|
||||||
expect(
|
|
||||||
Wrapper()
|
|
||||||
.find('img')
|
|
||||||
.attributes('src'),
|
|
||||||
).toBe('https://s3.amazonaws.com/uifaces/faces/twitter/sawalazar/128.jpg')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
<template>
|
|
||||||
<ds-avatar
|
|
||||||
:image="user && user.avatar | proxyApiUrl"
|
|
||||||
:name="userName"
|
|
||||||
class="avatar"
|
|
||||||
:size="size"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'HcAvatar',
|
|
||||||
props: {
|
|
||||||
user: { type: Object, default: null },
|
|
||||||
size: { type: String, default: 'small' },
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
userName() {
|
|
||||||
const { name } = this.user || {}
|
|
||||||
// The name is used to display the initials in case
|
|
||||||
// the image cannot be loaded.
|
|
||||||
return name
|
|
||||||
// If the name is undefined, then our styleguide will
|
|
||||||
// display an icon for the anonymous user.
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -42,9 +42,9 @@ describe('AvatarMenu.vue', () => {
|
|||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders the HcAvatar component', () => {
|
it('renders the UserAvatar component', () => {
|
||||||
wrapper.find('.avatar-menu-trigger').trigger('click')
|
wrapper.find('.avatar-menu-trigger').trigger('click')
|
||||||
expect(wrapper.find('.ds-avatar').exists()).toBe(true)
|
expect(wrapper.find('.user-avatar').exists()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('given a userName', () => {
|
describe('given a userName', () => {
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
"
|
"
|
||||||
@click.prevent="toggleMenu"
|
@click.prevent="toggleMenu"
|
||||||
>
|
>
|
||||||
<hc-avatar :user="user" />
|
<user-avatar :user="user" />
|
||||||
<base-icon class="dropdown-arrow" name="angle-down" />
|
<base-icon class="dropdown-arrow" name="angle-down" />
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
@ -49,12 +49,12 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import Dropdown from '~/components/Dropdown'
|
import Dropdown from '~/components/Dropdown'
|
||||||
import HcAvatar from '~/components/Avatar/Avatar.vue'
|
import UserAvatar from '~/components/_new/generic/UserAvatar/UserAvatar'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
HcAvatar,
|
UserAvatar,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
placement: { type: String, default: 'top-end' },
|
placement: { type: String, default: 'top-end' },
|
||||||
|
|||||||
@ -12,13 +12,13 @@
|
|||||||
<div v-else :class="{ comment: true, 'disabled-content': comment.deleted || comment.disabled }">
|
<div v-else :class="{ comment: true, 'disabled-content': comment.deleted || comment.disabled }">
|
||||||
<ds-card :id="anchor" :class="{ 'comment--target': isTarget }">
|
<ds-card :id="anchor" :class="{ 'comment--target': isTarget }">
|
||||||
<ds-space margin-bottom="small" margin-top="small">
|
<ds-space margin-bottom="small" margin-top="small">
|
||||||
<hc-user :user="author" :date-time="comment.createdAt">
|
<user-teaser :user="author" :date-time="comment.createdAt">
|
||||||
<template v-slot:dateTime>
|
<template v-slot:dateTime>
|
||||||
<ds-text v-if="comment.createdAt !== comment.updatedAt">
|
<ds-text v-if="comment.createdAt !== comment.updatedAt">
|
||||||
({{ $t('comment.edited') }})
|
({{ $t('comment.edited') }})
|
||||||
</ds-text>
|
</ds-text>
|
||||||
</template>
|
</template>
|
||||||
</hc-user>
|
</user-teaser>
|
||||||
<client-only>
|
<client-only>
|
||||||
<content-menu
|
<content-menu
|
||||||
v-show="!openEditCommentMenu"
|
v-show="!openEditCommentMenu"
|
||||||
@ -61,7 +61,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import { COMMENT_MAX_UNTRUNCATED_LENGTH, COMMENT_TRUNCATE_TO_LENGTH } from '~/constants/comment'
|
import { COMMENT_MAX_UNTRUNCATED_LENGTH, COMMENT_TRUNCATE_TO_LENGTH } from '~/constants/comment'
|
||||||
import HcUser from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
||||||
import ContentViewer from '~/components/Editor/ContentViewer'
|
import ContentViewer from '~/components/Editor/ContentViewer'
|
||||||
import HcCommentForm from '~/components/CommentForm/CommentForm'
|
import HcCommentForm from '~/components/CommentForm/CommentForm'
|
||||||
@ -82,7 +82,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
HcUser,
|
UserTeaser,
|
||||||
ContentMenu,
|
ContentMenu,
|
||||||
ContentViewer,
|
ContentViewer,
|
||||||
HcCommentForm,
|
HcCommentForm,
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
<ds-space />
|
<ds-space />
|
||||||
<client-only>
|
<client-only>
|
||||||
<hc-user :user="currentUser" :trunc="35" />
|
<user-teaser :user="currentUser" />
|
||||||
</client-only>
|
</client-only>
|
||||||
<ds-space />
|
<ds-space />
|
||||||
<ds-input
|
<ds-input
|
||||||
@ -122,14 +122,14 @@ import locales from '~/locales'
|
|||||||
import PostMutations from '~/graphql/PostMutations.js'
|
import PostMutations from '~/graphql/PostMutations.js'
|
||||||
import HcCategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect'
|
import HcCategoriesSelect from '~/components/CategoriesSelect/CategoriesSelect'
|
||||||
import HcTeaserImage from '~/components/TeaserImage/TeaserImage'
|
import HcTeaserImage from '~/components/TeaserImage/TeaserImage'
|
||||||
import HcUser from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
HcEditor,
|
HcEditor,
|
||||||
HcCategoriesSelect,
|
HcCategoriesSelect,
|
||||||
HcTeaserImage,
|
HcTeaserImage,
|
||||||
HcUser,
|
UserTeaser,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
contribution: { type: Object, default: () => {} },
|
contribution: { type: Object, default: () => {} },
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<ds-space :class="{ read: notification.read, notification: true }" margin-bottom="x-small">
|
<ds-space :class="{ read: notification.read, notification: true }" margin-bottom="x-small">
|
||||||
<client-only>
|
<client-only>
|
||||||
<ds-space margin-bottom="x-small">
|
<ds-space margin-bottom="x-small">
|
||||||
<hc-user :user="from.author" :date-time="from.createdAt" :trunc="35" />
|
<user-teaser :user="from.author" :date-time="from.createdAt" />
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<ds-text class="reason-text-for-test" color="soft">
|
<ds-text class="reason-text-for-test" color="soft">
|
||||||
{{ $t(`notifications.reason.${notification.reason}`) }}
|
{{ $t(`notifications.reason.${notification.reason}`) }}
|
||||||
@ -35,12 +35,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HcUser from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Notification',
|
name: 'Notification',
|
||||||
components: {
|
components: {
|
||||||
HcUser,
|
UserTeaser,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
notification: {
|
notification: {
|
||||||
|
|||||||
@ -89,8 +89,8 @@ describe('NotificationsTable.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('renders the author', () => {
|
it('renders the author', () => {
|
||||||
const username = firstRowNotification.find('.username')
|
const userinfo = firstRowNotification.find('.user-teaser > .info')
|
||||||
expect(username.text()).toEqual(postNotification.from.author.name)
|
expect(userinfo.text()).toContain(postNotification.from.author.name)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders the reason for the notification', () => {
|
it('renders the reason for the notification', () => {
|
||||||
@ -122,8 +122,8 @@ describe('NotificationsTable.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('renders the author', () => {
|
it('renders the author', () => {
|
||||||
const username = secondRowNotification.find('.username')
|
const userinfo = secondRowNotification.find('.user-teaser > .info')
|
||||||
expect(username.text()).toEqual(commentNotification.from.author.name)
|
expect(userinfo.text()).toContain(commentNotification.from.author.name)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders the reason for the notification', () => {
|
it('renders the reason for the notification', () => {
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { action } from '@storybook/addon-actions'
|
|||||||
import NotificationsTable from '~/components/NotificationsTable/NotificationsTable'
|
import NotificationsTable from '~/components/NotificationsTable/NotificationsTable'
|
||||||
import helpers from '~/storybook/helpers'
|
import helpers from '~/storybook/helpers'
|
||||||
import { post } from '~/components/PostCard/PostCard.story.js'
|
import { post } from '~/components/PostCard/PostCard.story.js'
|
||||||
import { user } from '~/components/User/User.story.js'
|
import { user } from '~/components/UserTeaser/UserTeaser.story.js'
|
||||||
|
|
||||||
helpers.init()
|
helpers.init()
|
||||||
export const notifications = [
|
export const notifications = [
|
||||||
|
|||||||
@ -15,10 +15,9 @@
|
|||||||
<template #user="scope">
|
<template #user="scope">
|
||||||
<ds-space margin-bottom="base">
|
<ds-space margin-bottom="base">
|
||||||
<client-only>
|
<client-only>
|
||||||
<hc-user
|
<user-teaser
|
||||||
:user="scope.row.from.author"
|
:user="scope.row.from.author"
|
||||||
:date-time="scope.row.from.createdAt"
|
:date-time="scope.row.from.createdAt"
|
||||||
:trunc="35"
|
|
||||||
:class="{ 'notification-status': scope.row.read }"
|
:class="{ 'notification-status': scope.row.read }"
|
||||||
/>
|
/>
|
||||||
</client-only>
|
</client-only>
|
||||||
@ -50,12 +49,12 @@
|
|||||||
<hc-empty v-else icon="alert" :message="$t('notifications.empty')" />
|
<hc-empty v-else icon="alert" :message="$t('notifications.empty')" />
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import HcUser from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
import HcEmpty from '~/components/Empty/Empty'
|
import HcEmpty from '~/components/Empty/Empty'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
HcUser,
|
UserTeaser,
|
||||||
HcEmpty,
|
HcEmpty,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@ -20,14 +20,14 @@
|
|||||||
<!-- Username, Image & Date of Post -->
|
<!-- Username, Image & Date of Post -->
|
||||||
<div class="user-wrapper">
|
<div class="user-wrapper">
|
||||||
<client-only>
|
<client-only>
|
||||||
<hc-user :user="post.author" :trunc="35" :date-time="post.createdAt" />
|
<user-teaser :user="post.author" :date-time="post.createdAt" />
|
||||||
</client-only>
|
</client-only>
|
||||||
<hc-ribbon v-if="isPinned" class="ribbon--pinned" :text="$t('post.pinned')" />
|
<hc-ribbon v-if="isPinned" class="ribbon--pinned" :text="$t('post.pinned')" />
|
||||||
<hc-ribbon v-else :text="$t('post.name')" />
|
<hc-ribbon v-else :text="$t('post.name')" />
|
||||||
</div>
|
</div>
|
||||||
<ds-space margin-bottom="small" />
|
<ds-space margin-bottom="small" />
|
||||||
<!-- Post Title -->
|
<!-- Post Title -->
|
||||||
<ds-heading tag="h3" no-margin class="hyphenate-text">{{ post.title }}</ds-heading>
|
<ds-heading tag="h3" class="hyphenate-text post-title">{{ post.title }}</ds-heading>
|
||||||
<ds-space margin-bottom="small" />
|
<ds-space margin-bottom="small" />
|
||||||
<!-- Post Content Excerpt -->
|
<!-- Post Content Excerpt -->
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
@ -78,7 +78,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HcUser from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
||||||
import HcCategory from '~/components/Category'
|
import HcCategory from '~/components/Category'
|
||||||
import HcRibbon from '~/components/Ribbon'
|
import HcRibbon from '~/components/Ribbon'
|
||||||
@ -89,7 +89,7 @@ import { postMenuModalsData, deletePostMutation } from '~/components/utils/PostH
|
|||||||
export default {
|
export default {
|
||||||
name: 'HcPostCard',
|
name: 'HcPostCard',
|
||||||
components: {
|
components: {
|
||||||
HcUser,
|
UserTeaser,
|
||||||
HcCategory,
|
HcCategory,
|
||||||
HcRibbon,
|
HcRibbon,
|
||||||
ContentMenu,
|
ContentMenu,
|
||||||
@ -186,7 +186,11 @@ export default {
|
|||||||
height: 75px;
|
height: 75px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* workaround to avoid jumping layout when hc-user is rendered */
|
.post-title {
|
||||||
|
margin-top: $space-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* workaround to avoid jumping layout when user-teaser is rendered */
|
||||||
.user-wrapper {
|
.user-wrapper {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { mount, RouterLinkStub } from '@vue/test-utils'
|
import { mount, RouterLinkStub } from '@vue/test-utils'
|
||||||
import User from './User.vue'
|
import UserTeaser from './UserTeaser.vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
@ -7,7 +7,7 @@ const filter = jest.fn(str => str)
|
|||||||
|
|
||||||
localVue.filter('truncate', filter)
|
localVue.filter('truncate', filter)
|
||||||
|
|
||||||
describe('User', () => {
|
describe('UserTeaser', () => {
|
||||||
let propsData
|
let propsData
|
||||||
let mocks
|
let mocks
|
||||||
let stubs
|
let stubs
|
||||||
@ -35,7 +35,7 @@ describe('User', () => {
|
|||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
getters,
|
getters,
|
||||||
})
|
})
|
||||||
return mount(User, { store, propsData, mocks, stubs, localVue })
|
return mount(UserTeaser, { store, propsData, mocks, stubs, localVue })
|
||||||
}
|
}
|
||||||
|
|
||||||
it('renders anonymous user', () => {
|
it('renders anonymous user', () => {
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { storiesOf } from '@storybook/vue'
|
import { storiesOf } from '@storybook/vue'
|
||||||
import { withA11y } from '@storybook/addon-a11y'
|
import { withA11y } from '@storybook/addon-a11y'
|
||||||
import User from '~/components/User/User.vue'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser.vue'
|
||||||
import helpers from '~/storybook/helpers'
|
import helpers from '~/storybook/helpers'
|
||||||
|
|
||||||
helpers.init()
|
helpers.init()
|
||||||
@ -53,25 +53,33 @@ export const user = {
|
|||||||
socialMedia: [],
|
socialMedia: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
storiesOf('User', module)
|
storiesOf('UserTeaser', module)
|
||||||
.addDecorator(withA11y)
|
.addDecorator(withA11y)
|
||||||
.addDecorator(helpers.layout)
|
.addDecorator(helpers.layout)
|
||||||
.add('available', () => ({
|
.add('user only', () => ({
|
||||||
components: { User },
|
components: { UserTeaser },
|
||||||
store: helpers.store,
|
store: helpers.store,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
user,
|
user,
|
||||||
}),
|
}),
|
||||||
template: '<user :user="user" :trunc="35" :date-time="new Date()" />',
|
template: '<user-teaser :user="user" />',
|
||||||
|
}))
|
||||||
|
.add('with Date', () => ({
|
||||||
|
components: { UserTeaser },
|
||||||
|
store: helpers.store,
|
||||||
|
data: () => ({
|
||||||
|
user,
|
||||||
|
}),
|
||||||
|
template: '<user-teaser :user="user" :date-time="new Date()" />',
|
||||||
}))
|
}))
|
||||||
.add('has edited something', () => ({
|
.add('has edited something', () => ({
|
||||||
components: { User },
|
components: { UserTeaser },
|
||||||
store: helpers.store,
|
store: helpers.store,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
user,
|
user,
|
||||||
}),
|
}),
|
||||||
template: `
|
template: `
|
||||||
<user :user="user" :trunc="35" :date-time="new Date()">
|
<user-teaser :user="user" :date-time="new Date()">
|
||||||
<template v-slot:dateTime>
|
<template v-slot:dateTime>
|
||||||
- HEY! I'm edited
|
- HEY! I'm edited
|
||||||
</template>
|
</template>
|
||||||
@ -79,10 +87,10 @@ storiesOf('User', module)
|
|||||||
`,
|
`,
|
||||||
}))
|
}))
|
||||||
.add('anonymous', () => ({
|
.add('anonymous', () => ({
|
||||||
components: { User },
|
components: { UserTeaser },
|
||||||
store: helpers.store,
|
store: helpers.store,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
user: null,
|
user: null,
|
||||||
}),
|
}),
|
||||||
template: '<user :user="user" :trunc="35" :date-time="new Date()" />',
|
template: '<user-teaser :user="user" :date-time="new Date()" />',
|
||||||
}))
|
}))
|
||||||
@ -1,32 +1,37 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="user" v-if="displayAnonymous">
|
<div class="user-teaser" v-if="displayAnonymous">
|
||||||
<hc-avatar v-if="showAvatar" class="avatar" />
|
<user-avatar v-if="showAvatar" />
|
||||||
<div>
|
<span class="info anonymous">{{ $t('profile.userAnonym') }}</span>
|
||||||
<b class="username">{{ $t('profile.userAnonym') }}</b>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<dropdown v-else :class="{ 'disabled-content': user.disabled }" placement="top-start" offset="0">
|
<dropdown
|
||||||
<template slot="default" slot-scope="{ openMenu, closeMenu, isOpen }">
|
v-else
|
||||||
<nuxt-link :to="userLink" :class="['user', isOpen && 'active']">
|
:class="[{ 'disabled-content': user.disabled }]"
|
||||||
<div @mouseover="showPopover ? openMenu(true) : () => {}" @mouseleave="closeMenu(true)">
|
placement="top-start"
|
||||||
<hc-avatar v-if="showAvatar" class="avatar" :user="user" />
|
offset="0"
|
||||||
<div>
|
>
|
||||||
<ds-text class="userinfo">
|
<template #default="{ openMenu, closeMenu, isOpen }">
|
||||||
<b>{{ userSlug }}</b>
|
<nuxt-link
|
||||||
</ds-text>
|
:to="userLink"
|
||||||
</div>
|
:class="['user-teaser', isOpen && 'active']"
|
||||||
<ds-text class="username" align="left" size="small" color="soft">
|
@mouseover.native="showPopover ? openMenu(true) : () => {}"
|
||||||
{{ userName | truncate(18) }}
|
@mouseleave.native="closeMenu(true)"
|
||||||
<template v-if="dateTime">
|
>
|
||||||
<base-icon name="clock" />
|
<user-avatar v-if="showAvatar" :user="user" size="small" />
|
||||||
<hc-relative-date-time :date-time="dateTime" />
|
<div class="info">
|
||||||
<slot name="dateTime"></slot>
|
<span class="text">
|
||||||
</template>
|
<span class="slug">{{ userSlug }}</span>
|
||||||
</ds-text>
|
<span v-if="dateTime">{{ userName }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="dateTime" class="text">
|
||||||
|
<base-icon name="clock" />
|
||||||
|
<hc-relative-date-time :date-time="dateTime" />
|
||||||
|
<slot name="dateTime"></slot>
|
||||||
|
</span>
|
||||||
|
<span v-else class="text">{{ userName }}</span>
|
||||||
</div>
|
</div>
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</template>
|
</template>
|
||||||
<template slot="popover" v-if="showPopover">
|
<template #popover v-if="showPopover">
|
||||||
<div style="min-width: 250px">
|
<div style="min-width: 250px">
|
||||||
<hc-badges v-if="user.badges && user.badges.length" :badges="user.badges" />
|
<hc-badges v-if="user.badges && user.badges.length" :badges="user.badges" />
|
||||||
<ds-text
|
<ds-text
|
||||||
@ -77,7 +82,6 @@
|
|||||||
/>
|
/>
|
||||||
</ds-flex-item>
|
</ds-flex-item>
|
||||||
</ds-flex>
|
</ds-flex>
|
||||||
<!--<ds-space margin-bottom="x-small" />-->
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</dropdown>
|
</dropdown>
|
||||||
@ -89,22 +93,21 @@ import { mapGetters } from 'vuex'
|
|||||||
import HcRelativeDateTime from '~/components/RelativeDateTime'
|
import HcRelativeDateTime from '~/components/RelativeDateTime'
|
||||||
import HcFollowButton from '~/components/FollowButton'
|
import HcFollowButton from '~/components/FollowButton'
|
||||||
import HcBadges from '~/components/Badges'
|
import HcBadges from '~/components/Badges'
|
||||||
import HcAvatar from '~/components/Avatar/Avatar.vue'
|
import UserAvatar from '~/components/_new/generic/UserAvatar/UserAvatar'
|
||||||
import Dropdown from '~/components/Dropdown'
|
import Dropdown from '~/components/Dropdown'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HcUser',
|
name: 'UserTeaser',
|
||||||
components: {
|
components: {
|
||||||
HcRelativeDateTime,
|
HcRelativeDateTime,
|
||||||
HcFollowButton,
|
HcFollowButton,
|
||||||
HcAvatar,
|
UserAvatar,
|
||||||
HcBadges,
|
HcBadges,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
user: { type: Object, default: null },
|
user: { type: Object, default: null },
|
||||||
showAvatar: { type: Boolean, default: true },
|
showAvatar: { type: Boolean, default: true },
|
||||||
trunc: { type: Number, default: 18 }, // "-1" is no trunc
|
|
||||||
dateTime: { type: [Date, String], default: null },
|
dateTime: { type: [Date, String], default: null },
|
||||||
showPopover: { type: Boolean, default: true },
|
showPopover: { type: Boolean, default: true },
|
||||||
},
|
},
|
||||||
@ -147,38 +150,51 @@ export default {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style lang="scss">
|
||||||
.avatar {
|
.trigger {
|
||||||
float: left;
|
max-width: 100%;
|
||||||
margin-right: 4px;
|
|
||||||
height: 100%;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.userinfo {
|
.user-teaser {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-wrap: nowrap;
|
||||||
|
z-index: $z-index-post-card-link;
|
||||||
> .ds-text {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-left: $space-xx-small;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.user {
|
|
||||||
white-space: nowrap;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&:hover,
|
> .user-avatar {
|
||||||
&.active {
|
flex-shrink: 0;
|
||||||
z-index: 999;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.user-slug {
|
> .info {
|
||||||
margin-bottom: $space-xx-small;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
padding-left: $space-xx-small;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
color: $text-color-soft;
|
||||||
|
font-size: $font-size-small;
|
||||||
|
|
||||||
|
&.anonymous {
|
||||||
|
font-size: $font-size-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slug {
|
||||||
|
color: $color-primary;
|
||||||
|
font-size: $font-size-base;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
|
> .ds-text {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
99
webapp/components/_new/generic/UserAvatar/UserAvatar.spec.js
Normal file
99
webapp/components/_new/generic/UserAvatar/UserAvatar.spec.js
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import UserAvatar from './UserAvatar.vue'
|
||||||
|
import BaseIcon from '~/components/_new/generic/BaseIcon/BaseIcon'
|
||||||
|
|
||||||
|
const localVue = global.localVue
|
||||||
|
|
||||||
|
describe('UserAvatar.vue', () => {
|
||||||
|
let propsData, wrapper
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData = {}
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
const Wrapper = () => {
|
||||||
|
return mount(UserAvatar, { propsData, localVue })
|
||||||
|
}
|
||||||
|
|
||||||
|
it('renders no image', () => {
|
||||||
|
expect(wrapper.find('img').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders an icon', () => {
|
||||||
|
expect(wrapper.find(BaseIcon).exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('given a user', () => {
|
||||||
|
describe('with no image', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData = {
|
||||||
|
user: {
|
||||||
|
name: 'Matt Rider',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('no user name', () => {
|
||||||
|
it('renders an icon', () => {
|
||||||
|
propsData = { user: { name: null } }
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.find(BaseIcon).exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("user name is 'Anonymous'", () => {
|
||||||
|
it('renders an icon', () => {
|
||||||
|
propsData = { user: { name: 'Anonymous' } }
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.find(BaseIcon).exists()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays user initials', () => {
|
||||||
|
expect(wrapper.find('.initials').text()).toEqual('MR')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays no more than 3 initials', () => {
|
||||||
|
propsData = { user: { name: 'Ana Paula Nunes Marques' } }
|
||||||
|
wrapper = Wrapper()
|
||||||
|
expect(wrapper.find('.initials').text()).toEqual('APN')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with a relative avatar url', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData = {
|
||||||
|
user: {
|
||||||
|
name: 'Not Anonymous',
|
||||||
|
avatar: '/avatar.jpg',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('adds a prefix to load the image from the uploads service', () => {
|
||||||
|
expect(wrapper.find('.image').attributes('src')).toBe('/api/avatar.jpg')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with an absolute avatar url', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
propsData = {
|
||||||
|
user: {
|
||||||
|
name: 'Not Anonymous',
|
||||||
|
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/sawalazar/128.jpg',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
wrapper = Wrapper()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('keeps the avatar URL as is', () => {
|
||||||
|
// e.g. our seeds have absolute image URLs
|
||||||
|
expect(wrapper.find('.image').attributes('src')).toBe(
|
||||||
|
'https://s3.amazonaws.com/uifaces/faces/twitter/sawalazar/128.jpg',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
import { storiesOf } from '@storybook/vue'
|
||||||
|
import { withA11y } from '@storybook/addon-a11y'
|
||||||
|
import StoryRouter from 'storybook-vue-router'
|
||||||
|
import UserAvatar from '~/components/_new/generic/UserAvatar/UserAvatar'
|
||||||
|
import helpers from '~/storybook/helpers'
|
||||||
|
import { user } from '~/components/UserTeaser/UserTeaser.story.js'
|
||||||
|
|
||||||
|
helpers.init()
|
||||||
|
const anonymousUser = {
|
||||||
|
...user,
|
||||||
|
name: 'Anonymous',
|
||||||
|
avatar: null,
|
||||||
|
}
|
||||||
|
const userWithoutAvatar = {
|
||||||
|
...user,
|
||||||
|
avatar: null,
|
||||||
|
name: 'Ana Paula Nunes Marques',
|
||||||
|
}
|
||||||
|
storiesOf('UserAvatar', module)
|
||||||
|
.addDecorator(withA11y)
|
||||||
|
.addDecorator(helpers.layout)
|
||||||
|
.addDecorator(StoryRouter())
|
||||||
|
.add('with image', () => ({
|
||||||
|
components: { UserAvatar },
|
||||||
|
data: () => ({
|
||||||
|
user,
|
||||||
|
}),
|
||||||
|
template: '<user-avatar :user="user" />',
|
||||||
|
}))
|
||||||
|
.add('without image, anonymous user', () => ({
|
||||||
|
components: { UserAvatar },
|
||||||
|
data: () => ({
|
||||||
|
user: anonymousUser,
|
||||||
|
}),
|
||||||
|
template: '<user-avatar :user="user" />',
|
||||||
|
}))
|
||||||
|
.add('without image, user initials', () => ({
|
||||||
|
components: { UserAvatar },
|
||||||
|
data: () => ({
|
||||||
|
user: userWithoutAvatar,
|
||||||
|
}),
|
||||||
|
template: '<user-avatar :user="user" />',
|
||||||
|
}))
|
||||||
|
.add('small', () => ({
|
||||||
|
components: { UserAvatar },
|
||||||
|
data: () => ({
|
||||||
|
user,
|
||||||
|
}),
|
||||||
|
template: '<user-avatar :user="user" size="small"/>',
|
||||||
|
}))
|
||||||
|
.add('large', () => ({
|
||||||
|
components: { UserAvatar },
|
||||||
|
data: () => ({
|
||||||
|
user,
|
||||||
|
}),
|
||||||
|
template: '<user-avatar :user="user" size="large"/>',
|
||||||
|
}))
|
||||||
84
webapp/components/_new/generic/UserAvatar/UserAvatar.vue
Normal file
84
webapp/components/_new/generic/UserAvatar/UserAvatar.vue
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="['user-avatar', size && `--${this.size}`]">
|
||||||
|
<span class="initials">{{ userInitials }}</span>
|
||||||
|
<base-icon v-if="isAnonymous" name="eye-slash" />
|
||||||
|
<img
|
||||||
|
v-else
|
||||||
|
:src="user.avatar | proxyApiUrl"
|
||||||
|
class="image"
|
||||||
|
@error="event.target.style.display = 'none'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'UserAvatar',
|
||||||
|
props: {
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
validator: value => {
|
||||||
|
return value.match(/(small|large)/)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isAnonymous() {
|
||||||
|
return !this.user || !this.user.name || this.user.name.toLowerCase() === 'anonymous'
|
||||||
|
},
|
||||||
|
userInitials() {
|
||||||
|
if (this.isAnonymous) return ''
|
||||||
|
|
||||||
|
return this.user.name
|
||||||
|
.match(/\b\w/g)
|
||||||
|
.join('')
|
||||||
|
.substring(0, 3)
|
||||||
|
.toUpperCase()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.user-avatar {
|
||||||
|
position: relative;
|
||||||
|
height: $size-avatar-base;
|
||||||
|
width: $size-avatar-base;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: $color-primary-dark;
|
||||||
|
color: $text-color-primary-inverse;
|
||||||
|
|
||||||
|
&.--small {
|
||||||
|
width: $size-avatar-small;
|
||||||
|
height: $size-avatar-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.--large {
|
||||||
|
width: $size-avatar-large;
|
||||||
|
height: $size-avatar-large;
|
||||||
|
font-size: $font-size-xx-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .initials,
|
||||||
|
> .base-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
> .image {
|
||||||
|
position: relative;
|
||||||
|
z-index: 5;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -7,11 +7,10 @@
|
|||||||
condensed
|
condensed
|
||||||
>
|
>
|
||||||
<template #submitter="scope">
|
<template #submitter="scope">
|
||||||
<hc-user
|
<user-teaser
|
||||||
:user="scope.row.submitter"
|
:user="scope.row.submitter"
|
||||||
:showAvatar="false"
|
:showAvatar="false"
|
||||||
:showPopover="false"
|
:showPopover="false"
|
||||||
:trunc="30"
|
|
||||||
data-test="filing-user"
|
data-test="filing-user"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -29,12 +28,12 @@
|
|||||||
</ds-table>
|
</ds-table>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import HcUser from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
import HcRelativeDateTime from '~/components/RelativeDateTime'
|
import HcRelativeDateTime from '~/components/RelativeDateTime'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
HcUser,
|
UserTeaser,
|
||||||
HcRelativeDateTime,
|
HcRelativeDateTime,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { storiesOf } from '@storybook/vue'
|
|||||||
import { withA11y } from '@storybook/addon-a11y'
|
import { withA11y } from '@storybook/addon-a11y'
|
||||||
import { action } from '@storybook/addon-actions'
|
import { action } from '@storybook/addon-actions'
|
||||||
import { post } from '~/components/PostCard/PostCard.story.js'
|
import { post } from '~/components/PostCard/PostCard.story.js'
|
||||||
import { user } from '~/components/User/User.story.js'
|
import { user } from '~/components/UserTeaser/UserTeaser.story.js'
|
||||||
import helpers from '~/storybook/helpers'
|
import helpers from '~/storybook/helpers'
|
||||||
import ReportList from './ReportList'
|
import ReportList from './ReportList'
|
||||||
import DropdownFilter from '~/components/DropdownFilter/DropdownFilter'
|
import DropdownFilter from '~/components/DropdownFilter/DropdownFilter'
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
<!-- Content Column -->
|
<!-- Content Column -->
|
||||||
<td class="ds-table-col" data-test="report-content">
|
<td class="ds-table-col" data-test="report-content">
|
||||||
<client-only v-if="isUser">
|
<client-only v-if="isUser">
|
||||||
<hc-user :user="report.resource" :showAvatar="false" :trunc="30" :showPopover="false" />
|
<user-teaser :user="report.resource" :showAvatar="false" :showPopover="false" />
|
||||||
</client-only>
|
</client-only>
|
||||||
<nuxt-link v-else class="title" :to="linkTarget">
|
<nuxt-link v-else class="title" :to="linkTarget">
|
||||||
{{ linkText | truncate(50) }}
|
{{ linkText | truncate(50) }}
|
||||||
@ -29,12 +29,7 @@
|
|||||||
<!-- Author Column -->
|
<!-- Author Column -->
|
||||||
<td class="ds-table-col" data-test="report-author">
|
<td class="ds-table-col" data-test="report-author">
|
||||||
<client-only v-if="!isUser">
|
<client-only v-if="!isUser">
|
||||||
<hc-user
|
<user-teaser :user="report.resource.author" :showAvatar="false" :showPopover="false" />
|
||||||
:user="report.resource.author"
|
|
||||||
:showAvatar="false"
|
|
||||||
:trunc="30"
|
|
||||||
:showPopover="false"
|
|
||||||
/>
|
|
||||||
</client-only>
|
</client-only>
|
||||||
<span v-else>—</span>
|
<span v-else>—</span>
|
||||||
</td>
|
</td>
|
||||||
@ -46,10 +41,9 @@
|
|||||||
{{ statusText }}
|
{{ statusText }}
|
||||||
</span>
|
</span>
|
||||||
<client-only v-if="isReviewed">
|
<client-only v-if="isReviewed">
|
||||||
<hc-user
|
<user-teaser
|
||||||
:user="moderatorOfLatestReview"
|
:user="moderatorOfLatestReview"
|
||||||
:showAvatar="false"
|
:showAvatar="false"
|
||||||
:trunc="30"
|
|
||||||
:date-time="report.updatedAt"
|
:date-time="report.updatedAt"
|
||||||
:showPopover="false"
|
:showPopover="false"
|
||||||
/>
|
/>
|
||||||
@ -85,12 +79,12 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import FiledReportsTable from '~/components/features/FiledReportsTable/FiledReportsTable'
|
import FiledReportsTable from '~/components/features/FiledReportsTable/FiledReportsTable'
|
||||||
import HcUser from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
FiledReportsTable,
|
FiledReportsTable,
|
||||||
HcUser,
|
UserTeaser,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
report: {
|
report: {
|
||||||
|
|||||||
@ -97,8 +97,8 @@ describe('SearchableInput.vue', () => {
|
|||||||
it("pushes to user's profile", async () => {
|
it("pushes to user's profile", async () => {
|
||||||
select.element.value = 'Bob'
|
select.element.value = 'Bob'
|
||||||
select.trigger('input')
|
select.trigger('input')
|
||||||
const users = wrapper.findAll('.userinfo')
|
const users = wrapper.findAll('.slug')
|
||||||
const bob = users.filter(item => item.text() === '@bob-der-baumeister')
|
const bob = users.filter(item => item.text().match(/@bob-der-baumeister/))
|
||||||
bob.trigger('click')
|
bob.trigger('click')
|
||||||
await Vue.nextTick()
|
await Vue.nextTick()
|
||||||
expect(mocks.$router.push).toHaveBeenCalledWith({
|
expect(mocks.$router.push).toHaveBeenCalledWith({
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
v-if="option.__typename === 'User'"
|
v-if="option.__typename === 'User'"
|
||||||
:class="{ 'option-with-heading': isFirstOfType(option) }"
|
:class="{ 'option-with-heading': isFirstOfType(option) }"
|
||||||
>
|
>
|
||||||
<hc-user :user="option" :showPopover="false" />
|
<user-teaser :user="option" :showPopover="false" />
|
||||||
</p>
|
</p>
|
||||||
<p
|
<p
|
||||||
v-if="option.__typename === 'Post'"
|
v-if="option.__typename === 'Post'"
|
||||||
@ -45,14 +45,14 @@
|
|||||||
import { isEmpty } from 'lodash'
|
import { isEmpty } from 'lodash'
|
||||||
import SearchHeading from '~/components/generic/SearchHeading/SearchHeading.vue'
|
import SearchHeading from '~/components/generic/SearchHeading/SearchHeading.vue'
|
||||||
import SearchPost from '~/components/generic/SearchPost/SearchPost.vue'
|
import SearchPost from '~/components/generic/SearchPost/SearchPost.vue'
|
||||||
import HcUser from '~/components/User/User.vue'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SearchableInput',
|
name: 'SearchableInput',
|
||||||
components: {
|
components: {
|
||||||
SearchHeading,
|
SearchHeading,
|
||||||
SearchPost,
|
SearchPost,
|
||||||
HcUser,
|
UserTeaser,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
id: { type: String },
|
id: { type: String },
|
||||||
|
|||||||
@ -19,11 +19,11 @@
|
|||||||
@click="blurred = !blurred"
|
@click="blurred = !blurred"
|
||||||
/>
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
<hc-user :user="post.author" :date-time="post.createdAt">
|
<user-teaser :user="post.author" :date-time="post.createdAt">
|
||||||
<template v-slot:dateTime>
|
<template v-slot:dateTime>
|
||||||
<ds-text v-if="post.createdAt !== post.updatedAt">({{ $t('post.edited') }})</ds-text>
|
<ds-text v-if="post.createdAt !== post.updatedAt">({{ $t('post.edited') }})</ds-text>
|
||||||
</template>
|
</template>
|
||||||
</hc-user>
|
</user-teaser>
|
||||||
<client-only>
|
<client-only>
|
||||||
<content-menu
|
<content-menu
|
||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
@ -101,7 +101,7 @@ import ContentViewer from '~/components/Editor/ContentViewer'
|
|||||||
import HcCategory from '~/components/Category'
|
import HcCategory from '~/components/Category'
|
||||||
import HcHashtag from '~/components/Hashtag/Hashtag'
|
import HcHashtag from '~/components/Hashtag/Hashtag'
|
||||||
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
||||||
import HcUser from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
import HcShoutButton from '~/components/ShoutButton.vue'
|
import HcShoutButton from '~/components/ShoutButton.vue'
|
||||||
import HcCommentForm from '~/components/CommentForm/CommentForm'
|
import HcCommentForm from '~/components/CommentForm/CommentForm'
|
||||||
import HcCommentList from '~/components/CommentList/CommentList'
|
import HcCommentList from '~/components/CommentList/CommentList'
|
||||||
@ -119,7 +119,7 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
HcCategory,
|
HcCategory,
|
||||||
HcHashtag,
|
HcHashtag,
|
||||||
HcUser,
|
UserTeaser,
|
||||||
HcShoutButton,
|
HcShoutButton,
|
||||||
ContentMenu,
|
ContentMenu,
|
||||||
HcCommentForm,
|
HcCommentForm,
|
||||||
|
|||||||
@ -11,9 +11,9 @@
|
|||||||
style="position: relative; height: auto;"
|
style="position: relative; height: auto;"
|
||||||
>
|
>
|
||||||
<hc-upload v-if="myProfile" :user="user">
|
<hc-upload v-if="myProfile" :user="user">
|
||||||
<hc-avatar :user="user" class="profile-avatar" size="x-large"></hc-avatar>
|
<user-avatar :user="user" class="profile-avatar" size="large"></user-avatar>
|
||||||
</hc-upload>
|
</hc-upload>
|
||||||
<hc-avatar v-else :user="user" class="profile-avatar" size="x-large" />
|
<user-avatar v-else :user="user" class="profile-avatar" size="large" />
|
||||||
<!-- Menu -->
|
<!-- Menu -->
|
||||||
<client-only>
|
<client-only>
|
||||||
<content-menu
|
<content-menu
|
||||||
@ -99,7 +99,7 @@
|
|||||||
<ds-space v-for="follow in uniq(user.following)" :key="follow.id" margin="x-small">
|
<ds-space v-for="follow in uniq(user.following)" :key="follow.id" margin="x-small">
|
||||||
<!-- TODO: find better solution for rendering errors -->
|
<!-- TODO: find better solution for rendering errors -->
|
||||||
<client-only>
|
<client-only>
|
||||||
<user :user="follow" :trunc="15" />
|
<user-teaser :user="follow" />
|
||||||
</client-only>
|
</client-only>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<ds-space v-if="user.followingCount - user.following.length" margin="small">
|
<ds-space v-if="user.followingCount - user.following.length" margin="small">
|
||||||
@ -129,7 +129,7 @@
|
|||||||
<ds-space v-for="follow in uniq(user.followedBy)" :key="follow.id" margin="x-small">
|
<ds-space v-for="follow in uniq(user.followedBy)" :key="follow.id" margin="x-small">
|
||||||
<!-- TODO: find better solution for rendering errors -->
|
<!-- TODO: find better solution for rendering errors -->
|
||||||
<client-only>
|
<client-only>
|
||||||
<user :user="follow" :trunc="15" />
|
<user-teaser :user="follow" />
|
||||||
</client-only>
|
</client-only>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
<ds-space v-if="user.followedByCount - user.followedBy.length" margin="small">
|
<ds-space v-if="user.followedByCount - user.followedBy.length" margin="small">
|
||||||
@ -157,7 +157,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<ds-space v-for="link in socialMediaLinks" :key="link.username" margin="x-small">
|
<ds-space v-for="link in socialMediaLinks" :key="link.username" margin="x-small">
|
||||||
<a :href="link.url" target="_blank">
|
<a :href="link.url" target="_blank">
|
||||||
<ds-avatar :image="link.favicon" />
|
<user-avatar :image="link.favicon" />
|
||||||
{{ link.username }}
|
{{ link.username }}
|
||||||
</a>
|
</a>
|
||||||
</ds-space>
|
</ds-space>
|
||||||
@ -271,7 +271,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import uniqBy from 'lodash/uniqBy'
|
import uniqBy from 'lodash/uniqBy'
|
||||||
import User from '~/components/User/User'
|
import UserTeaser from '~/components/UserTeaser/UserTeaser'
|
||||||
import HcPostCard from '~/components/PostCard/PostCard.vue'
|
import HcPostCard from '~/components/PostCard/PostCard.vue'
|
||||||
import HcFollowButton from '~/components/FollowButton.vue'
|
import HcFollowButton from '~/components/FollowButton.vue'
|
||||||
import HcCountTo from '~/components/CountTo.vue'
|
import HcCountTo from '~/components/CountTo.vue'
|
||||||
@ -279,7 +279,7 @@ import HcBadges from '~/components/Badges.vue'
|
|||||||
import HcEmpty from '~/components/Empty/Empty'
|
import HcEmpty from '~/components/Empty/Empty'
|
||||||
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
import ContentMenu from '~/components/ContentMenu/ContentMenu'
|
||||||
import HcUpload from '~/components/Upload'
|
import HcUpload from '~/components/Upload'
|
||||||
import HcAvatar from '~/components/Avatar/Avatar.vue'
|
import UserAvatar from '~/components/_new/generic/UserAvatar/UserAvatar'
|
||||||
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
|
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
|
||||||
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
|
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
|
||||||
import { profilePagePosts } from '~/graphql/PostQuery'
|
import { profilePagePosts } from '~/graphql/PostQuery'
|
||||||
@ -297,15 +297,14 @@ const tabToFilterMapping = ({ tab, id }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'HcUserProfile',
|
|
||||||
components: {
|
components: {
|
||||||
User,
|
UserTeaser,
|
||||||
HcPostCard,
|
HcPostCard,
|
||||||
HcFollowButton,
|
HcFollowButton,
|
||||||
HcCountTo,
|
HcCountTo,
|
||||||
HcBadges,
|
HcBadges,
|
||||||
HcEmpty,
|
HcEmpty,
|
||||||
HcAvatar,
|
UserAvatar,
|
||||||
ContentMenu,
|
ContentMenu,
|
||||||
HcUpload,
|
HcUpload,
|
||||||
MasonryGrid,
|
MasonryGrid,
|
||||||
@ -525,11 +524,9 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.profile-avatar.ds-avatar {
|
.profile-avatar.user-avatar {
|
||||||
display: block;
|
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-top: -60px;
|
margin-top: -60px;
|
||||||
border: #fff 5px solid;
|
|
||||||
}
|
}
|
||||||
.page-name-profile-id-slug {
|
.page-name-profile-id-slug {
|
||||||
.ds-flex-item:first-child .content-menu {
|
.ds-flex-item:first-child .content-menu {
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
params: { id: scope.row.id, slug: scope.row.slug },
|
params: { id: scope.row.id, slug: scope.row.slug },
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<hc-avatar :user="scope.row" size="small" />
|
<user-avatar :user="scope.row" size="small" />
|
||||||
</nuxt-link>
|
</nuxt-link>
|
||||||
</template>
|
</template>
|
||||||
<template slot="name" slot-scope="scope">
|
<template slot="name" slot-scope="scope">
|
||||||
@ -70,11 +70,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { mutedUsers, unmuteUser } from '~/graphql/settings/MutedUsers'
|
import { mutedUsers, unmuteUser } from '~/graphql/settings/MutedUsers'
|
||||||
import HcAvatar from '~/components/Avatar/Avatar.vue'
|
import UserAvatar from '~/components/_new/generic/UserAvatar/UserAvatar'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
HcAvatar,
|
UserAvatar,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user