From f192684fef9b05256508fb8eddc6508ed5a9a709 Mon Sep 17 00:00:00 2001 From: Gabriela Ueno Date: Wed, 3 Apr 2024 08:03:15 -0300 Subject: [PATCH] ASAP-21 (GP2) Add tags to news (#4218) --- .../workflows/reusable-gp2-algolia-sync.yml | 2 + apps/gp2-frontend/src/news/NewsList.tsx | 6 +- apps/gp2-frontend/src/tags/ResultList.tsx | 5 + .../src/tags/__tests__/ResultList.test.tsx | 29 ++- .../src/data-providers/news.data-provider.ts | 3 + .../data-providers/news.data-provider.test.ts | 12 + .../gp2-server/test/fixtures/news.fixtures.ts | 3 + .../gp2/news/20240326151708-add-tags-field.js | 32 +++ .../src/gp2/autogenerated-gql/gql.ts | 6 +- .../src/gp2/autogenerated-gql/graphql.ts | 211 ++++++++++++++++-- .../src/gp2/queries/news.queries.ts | 9 + .../gp2/schema/autogenerated-schema.graphql | 136 +++++++++-- packages/fixtures/src/gp2/news.ts | 3 + packages/gp2-components/src/index.ts | 1 - .../gp2-components/src/molecules/NewsItem.tsx | 106 --------- .../src/molecules/__tests__/NewsItem.test.tsx | 36 --- .../gp2-components/src/molecules/index.ts | 1 - .../src/templates/DashboardPageBody.tsx | 4 +- .../src/templates/TagSearchPageList.tsx | 1 + packages/model/src/gp2/news.ts | 1 + .../src/organisms/NewsCard.tsx | 51 +++-- 21 files changed, 443 insertions(+), 215 deletions(-) create mode 100644 packages/contentful/migrations/gp2/news/20240326151708-add-tags-field.js delete mode 100644 packages/gp2-components/src/molecules/NewsItem.tsx delete mode 100644 packages/gp2-components/src/molecules/__tests__/NewsItem.test.tsx diff --git a/.github/workflows/reusable-gp2-algolia-sync.yml b/.github/workflows/reusable-gp2-algolia-sync.yml index 06df11ed05..3227a5b470 100644 --- a/.github/workflows/reusable-gp2-algolia-sync.yml +++ b/.github/workflows/reusable-gp2-algolia-sync.yml @@ -50,6 +50,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 + - name: Re-Build + uses: ./.github/actions/build-rebuild - name: Cache build typecheck output uses: ./.github/actions/cache-unplugged with: diff --git a/apps/gp2-frontend/src/news/NewsList.tsx b/apps/gp2-frontend/src/news/NewsList.tsx index 892799c99c..81ca090fb2 100644 --- a/apps/gp2-frontend/src/news/NewsList.tsx +++ b/apps/gp2-frontend/src/news/NewsList.tsx @@ -1,5 +1,5 @@ -import { NewsItem, EmptyState, noNewsIcon } from '@asap-hub/gp2-components'; -import { ResultList } from '@asap-hub/react-components'; +import { EmptyState, noNewsIcon } from '@asap-hub/gp2-components'; +import { NewsCard, ResultList } from '@asap-hub/react-components'; import { useNews } from './state'; import { usePagination, usePaginationParams } from '../hooks/pagination'; @@ -26,7 +26,7 @@ const NewsList: React.FC = ({ searchQuery, filters }) => { renderPageHref={renderPageHref} > {items.map((news) => ( - + ))} ) : ( diff --git a/apps/gp2-frontend/src/tags/ResultList.tsx b/apps/gp2-frontend/src/tags/ResultList.tsx index 1f77b4efa4..4c16e32b01 100644 --- a/apps/gp2-frontend/src/tags/ResultList.tsx +++ b/apps/gp2-frontend/src/tags/ResultList.tsx @@ -8,6 +8,7 @@ import { import { gp2 as gp2Model } from '@asap-hub/model'; import { EventCard, + NewsCard, ResultList as ResultListComponent, } from '@asap-hub/react-components'; import { eventMapper } from '../events/EventsList'; @@ -67,6 +68,10 @@ const ResultList: React.FC = ({ filters = new Set() }) => { const tagData = data.tags.map((tag) => tag.name); return ; } + case 'news': { + const data = result as gp2Model.NewsResponse; + return ; + } default: return ''; } diff --git a/apps/gp2-frontend/src/tags/__tests__/ResultList.test.tsx b/apps/gp2-frontend/src/tags/__tests__/ResultList.test.tsx index 4d64a463c6..3953ce41eb 100644 --- a/apps/gp2-frontend/src/tags/__tests__/ResultList.test.tsx +++ b/apps/gp2-frontend/src/tags/__tests__/ResultList.test.tsx @@ -1,3 +1,4 @@ +import { ClientSearchResponse } from '@asap-hub/algolia'; import { render, screen, @@ -91,14 +92,34 @@ describe('ResultList', () => { expect(screen.getByRole('link', { name: 'Output 1' })).toBeInTheDocument(); }); - it('does not render unsupported types', async () => { + it('renders news', async () => { mockGetTagSearchResults.mockResolvedValue( createNewsListAlgoliaResponse(1, 1), ); + await renderList({}, 'test'); + expect(screen.getByText('News Item')).toBeInTheDocument(); + }); + + it('does not render unsupported types', async () => { + const response = createAlgoliaResponse<'external-user'>([ + { + __meta: { type: 'external-user' }, + displayName: 'John Doe', + id: 'external-1', + objectID: '1', + }, + ]); + + mockGetTagSearchResults.mockResolvedValue( + response as unknown as ClientSearchResponse< + 'gp2', + 'output' | 'event' | 'user' | 'news' | 'project' + >, + ); + await renderList({ filters: new Set() }, 'test'); - expect( - screen.queryByRole('link', { name: 'News 1' }), - ).not.toBeInTheDocument(); + + expect(screen.queryByText('John Doe')).not.toBeInTheDocument(); }); it('renders empty state', async () => { diff --git a/apps/gp2-server/src/data-providers/news.data-provider.ts b/apps/gp2-server/src/data-providers/news.data-provider.ts index abffb6273c..d30dc7724a 100644 --- a/apps/gp2-server/src/data-providers/news.data-provider.ts +++ b/apps/gp2-server/src/data-providers/news.data-provider.ts @@ -62,4 +62,7 @@ export const parseGraphQlNews = (item: NewsItem): gp2Model.NewsDataObject => ({ linkText: item.linkText ?? undefined, created: item.publishDate, type: item.type as gp2Model.NewsType, + tags: + item.tagsCollection?.items.map((tag) => tag?.name || '').filter(Boolean) || + [], }); diff --git a/apps/gp2-server/test/data-providers/news.data-provider.test.ts b/apps/gp2-server/test/data-providers/news.data-provider.test.ts index ef19693f73..8dad222d78 100644 --- a/apps/gp2-server/test/data-providers/news.data-provider.test.ts +++ b/apps/gp2-server/test/data-providers/news.data-provider.test.ts @@ -108,6 +108,18 @@ describe('News data provider', () => { ); }); + describe('tags', () => { + test('should return empty list when tagsCollection is null', async () => { + const newsResponse = getContentfulNewsGraphqlResponse(); + newsResponse.newsCollection!.items[0]!.tagsCollection = null; + + contentfulGraphqlClientMock.request.mockResolvedValueOnce(newsResponse); + const result = await newsDataProvider.fetch(); + + expect(result.items[0]!.tags).toEqual([]); + }); + }); + describe('Search', () => { test('Should query data properly when passing search param', async () => { contentfulGraphqlClientMock.request.mockResolvedValueOnce( diff --git a/apps/gp2-server/test/fixtures/news.fixtures.ts b/apps/gp2-server/test/fixtures/news.fixtures.ts index 06bb90166a..cee0688dee 100644 --- a/apps/gp2-server/test/fixtures/news.fixtures.ts +++ b/apps/gp2-server/test/fixtures/news.fixtures.ts @@ -10,6 +10,7 @@ import { } from '@asap-hub/model'; import { EventBridgeEvent, SQSEvent, SQSRecord } from 'aws-lambda'; import { createEventBridgeEventMock } from '../helpers/events'; +import { getContentfulTagsCollectionGraphqlResponse } from './tag.fixtures'; export const getContentfulGraphqlNews = (): NonNullable< NonNullable['items'][number] @@ -27,6 +28,7 @@ export const getContentfulGraphqlNews = (): NonNullable< }, publishDate: '2021-12-28T00:00:00.000Z', type: 'news', + tagsCollection: { ...getContentfulTagsCollectionGraphqlResponse() }, }); export const getContentfulNewsGraphqlResponse = @@ -46,6 +48,7 @@ export const getNewsDataObject = (): gp2Model.NewsDataObject => ({ link: 'http://example.com/a-link', linkText: 'some link text', type: 'news', + tags: ['tag-1'], }); export const getListNewsDataObject = (): gp2Model.ListNewsDataObject => ({ diff --git a/packages/contentful/migrations/gp2/news/20240326151708-add-tags-field.js b/packages/contentful/migrations/gp2/news/20240326151708-add-tags-field.js new file mode 100644 index 0000000000..ad8a6926e5 --- /dev/null +++ b/packages/contentful/migrations/gp2/news/20240326151708-add-tags-field.js @@ -0,0 +1,32 @@ +module.exports.description = 'Add tags to news content model'; + +module.exports.up = (migration) => { + const news = migration.editContentType('news'); + + news + .createField('tags') + .name('Tags') + .type('Array') + .localized(false) + .required(false) + .validations([]) + .disabled(false) + .omitted(false) + .items({ + type: 'Link', + + validations: [ + { + linkContentType: ['tags'], + }, + ], + + linkType: 'Entry', + }); + + news.moveField('tags').afterField('linkText'); +}; + +module.exports.down = (migration) => { + migration.editContentType('news').deleteField('tags'); +}; diff --git a/packages/contentful/src/gp2/autogenerated-gql/gql.ts b/packages/contentful/src/gp2/autogenerated-gql/gql.ts index a05a22e891..3e5072c6b1 100644 --- a/packages/contentful/src/gp2/autogenerated-gql/gql.ts +++ b/packages/contentful/src/gp2/autogenerated-gql/gql.ts @@ -49,7 +49,7 @@ const documents = { types.FetchExternalUsersDocument, '\n query FetchExternalUserById($id: String!) {\n externalUsers(id: $id) {\n ...ExternalUsersContentData\n }\n }\n \n': types.FetchExternalUserByIdDocument, - '\n fragment NewsContentData on News {\n sys {\n id\n firstPublishedAt\n }\n title\n shortText\n thumbnail {\n url\n }\n link\n linkText\n publishDate\n type\n }\n': + '\n fragment NewsContentData on News {\n sys {\n id\n firstPublishedAt\n }\n title\n shortText\n thumbnail {\n url\n }\n tagsCollection(limit: 20) {\n total\n items {\n sys {\n id\n }\n name\n }\n }\n link\n linkText\n publishDate\n type\n }\n': types.NewsContentDataFragmentDoc, '\n query FetchNewsById($id: String!) {\n news(id: $id) {\n ...NewsContentData\n }\n }\n \n': types.FetchNewsByIdDocument, @@ -245,8 +245,8 @@ export function gql( * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function gql( - source: '\n fragment NewsContentData on News {\n sys {\n id\n firstPublishedAt\n }\n title\n shortText\n thumbnail {\n url\n }\n link\n linkText\n publishDate\n type\n }\n', -): (typeof documents)['\n fragment NewsContentData on News {\n sys {\n id\n firstPublishedAt\n }\n title\n shortText\n thumbnail {\n url\n }\n link\n linkText\n publishDate\n type\n }\n']; + source: '\n fragment NewsContentData on News {\n sys {\n id\n firstPublishedAt\n }\n title\n shortText\n thumbnail {\n url\n }\n tagsCollection(limit: 20) {\n total\n items {\n sys {\n id\n }\n name\n }\n }\n link\n linkText\n publishDate\n type\n }\n', +): (typeof documents)['\n fragment NewsContentData on News {\n sys {\n id\n firstPublishedAt\n }\n title\n shortText\n thumbnail {\n url\n }\n tagsCollection(limit: 20) {\n total\n items {\n sys {\n id\n }\n name\n }\n }\n link\n linkText\n publishDate\n type\n }\n']; /** * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/contentful/src/gp2/autogenerated-gql/graphql.ts b/packages/contentful/src/gp2/autogenerated-gql/graphql.ts index 96e9250abd..cc64ed0a39 100644 --- a/packages/contentful/src/gp2/autogenerated-gql/graphql.ts +++ b/packages/contentful/src/gp2/autogenerated-gql/graphql.ts @@ -1676,9 +1676,21 @@ export type EventsNotesLinks = { }; export type EventsNotesResources = { - block: Array; - hyperlink: Array; - inline: Array; + block: Array; + hyperlink: Array; + inline: Array; +}; + +export type EventsNotesResourcesBlock = ResourceLink & { + sys: ResourceSys; +}; + +export type EventsNotesResourcesHyperlink = ResourceLink & { + sys: ResourceSys; +}; + +export type EventsNotesResourcesInline = ResourceLink & { + sys: ResourceSys; }; export enum EventsOrder { @@ -1753,9 +1765,21 @@ export type EventsPresentationLinks = { }; export type EventsPresentationResources = { - block: Array; - hyperlink: Array; - inline: Array; + block: Array; + hyperlink: Array; + inline: Array; +}; + +export type EventsPresentationResourcesBlock = ResourceLink & { + sys: ResourceSys; +}; + +export type EventsPresentationResourcesHyperlink = ResourceLink & { + sys: ResourceSys; +}; + +export type EventsPresentationResourcesInline = ResourceLink & { + sys: ResourceSys; }; export type EventsSpeakersCollection = { @@ -1821,9 +1845,21 @@ export type EventsVideoRecordingLinks = { }; export type EventsVideoRecordingResources = { - block: Array; - hyperlink: Array; - inline: Array; + block: Array; + hyperlink: Array; + inline: Array; +}; + +export type EventsVideoRecordingResourcesBlock = ResourceLink & { + sys: ResourceSys; +}; + +export type EventsVideoRecordingResourcesHyperlink = ResourceLink & { + sys: ResourceSys; +}; + +export type EventsVideoRecordingResourcesInline = ResourceLink & { + sys: ResourceSys; }; /** [See type definition](https://app.contentful.com/spaces/6ekgyp1432o9/content_types/externalUsers) */ @@ -2777,6 +2813,7 @@ export type News = Entry & { publishDate?: Maybe; shortText?: Maybe; sys: Sys; + tagsCollection?: Maybe; thumbnail?: Maybe; title?: Maybe; type?: Maybe; @@ -2807,6 +2844,16 @@ export type NewsShortTextArgs = { locale?: InputMaybe; }; +/** Hub News [See type definition](https://app.contentful.com/spaces/6ekgyp1432o9/content_types/news) */ +export type NewsTagsCollectionArgs = { + limit?: InputMaybe; + locale?: InputMaybe; + order?: InputMaybe>>; + preview?: InputMaybe; + skip?: InputMaybe; + where?: InputMaybe; +}; + /** Hub News [See type definition](https://app.contentful.com/spaces/6ekgyp1432o9/content_types/news) */ export type NewsThumbnailArgs = { locale?: InputMaybe; @@ -2865,6 +2912,8 @@ export type NewsFilter = { shortText_not_contains?: InputMaybe; shortText_not_in?: InputMaybe>>; sys?: InputMaybe; + tags?: InputMaybe; + tagsCollection_exists?: InputMaybe; thumbnail_exists?: InputMaybe; title?: InputMaybe; title_contains?: InputMaybe; @@ -2912,6 +2961,26 @@ export enum NewsOrder { TypeDesc = 'type_DESC', } +export type NewsTagsCollection = { + items: Array>; + limit: Scalars['Int']; + skip: Scalars['Int']; + total: Scalars['Int']; +}; + +export enum NewsTagsCollectionOrder { + NameAsc = 'name_ASC', + NameDesc = 'name_DESC', + SysFirstPublishedAtAsc = 'sys_firstPublishedAt_ASC', + SysFirstPublishedAtDesc = 'sys_firstPublishedAt_DESC', + SysIdAsc = 'sys_id_ASC', + SysIdDesc = 'sys_id_DESC', + SysPublishedAtAsc = 'sys_publishedAt_ASC', + SysPublishedAtDesc = 'sys_publishedAt_DESC', + SysPublishedVersionAsc = 'sys_publishedVersion_ASC', + SysPublishedVersionDesc = 'sys_publishedVersion_DESC', +} + /** [See type definition](https://app.contentful.com/spaces/6ekgyp1432o9/content_types/outputVersion) */ export type OutputVersion = Entry & { addedDate?: Maybe; @@ -3900,9 +3969,21 @@ export type PagesTextLinks = { }; export type PagesTextResources = { - block: Array; - hyperlink: Array; - inline: Array; + block: Array; + hyperlink: Array; + inline: Array; +}; + +export type PagesTextResourcesBlock = ResourceLink & { + sys: ResourceSys; +}; + +export type PagesTextResourcesHyperlink = ResourceLink & { + sys: ResourceSys; +}; + +export type PagesTextResourcesInline = ResourceLink & { + sys: ResourceSys; }; /** [See type definition](https://app.contentful.com/spaces/6ekgyp1432o9/content_types/projectMembership) */ @@ -4956,7 +5037,6 @@ export type ResourceLink = { export type ResourceSys = { linkType: Scalars['String']; - type: Scalars['String']; urn: Scalars['String']; }; @@ -5234,6 +5314,7 @@ export type TagsFilter = { export type TagsLinkingCollections = { entryCollection?: Maybe; eventsCollection?: Maybe; + newsCollection?: Maybe; outputsCollection?: Maybe; projectsCollection?: Maybe; usersCollection?: Maybe; @@ -5257,6 +5338,16 @@ export type TagsLinkingCollectionsEventsCollectionArgs = { skip?: InputMaybe; }; +export type TagsLinkingCollectionsNewsCollectionArgs = { + limit?: InputMaybe; + locale?: InputMaybe; + order?: InputMaybe< + Array> + >; + preview?: InputMaybe; + skip?: InputMaybe; +}; + export type TagsLinkingCollectionsOutputsCollectionArgs = { limit?: InputMaybe; locale?: InputMaybe; @@ -5346,6 +5437,25 @@ export enum TagsLinkingCollectionsEventsCollectionOrder { VideoRecordingUpdatedAtDesc = 'videoRecordingUpdatedAt_DESC', } +export enum TagsLinkingCollectionsNewsCollectionOrder { + LinkTextAsc = 'linkText_ASC', + LinkTextDesc = 'linkText_DESC', + LinkAsc = 'link_ASC', + LinkDesc = 'link_DESC', + PublishDateAsc = 'publishDate_ASC', + PublishDateDesc = 'publishDate_DESC', + SysFirstPublishedAtAsc = 'sys_firstPublishedAt_ASC', + SysFirstPublishedAtDesc = 'sys_firstPublishedAt_DESC', + SysIdAsc = 'sys_id_ASC', + SysIdDesc = 'sys_id_DESC', + SysPublishedAtAsc = 'sys_publishedAt_ASC', + SysPublishedAtDesc = 'sys_publishedAt_DESC', + SysPublishedVersionAsc = 'sys_publishedVersion_ASC', + SysPublishedVersionDesc = 'sys_publishedVersion_DESC', + TypeAsc = 'type_ASC', + TypeDesc = 'type_DESC', +} + export enum TagsLinkingCollectionsOutputsCollectionOrder { AccessionNumberAsc = 'accessionNumber_ASC', AccessionNumberDesc = 'accessionNumber_DESC', @@ -6746,9 +6856,21 @@ export type WorkingGroupsDescriptionLinks = { }; export type WorkingGroupsDescriptionResources = { - block: Array; - hyperlink: Array; - inline: Array; + block: Array; + hyperlink: Array; + inline: Array; +}; + +export type WorkingGroupsDescriptionResourcesBlock = ResourceLink & { + sys: ResourceSys; +}; + +export type WorkingGroupsDescriptionResourcesHyperlink = ResourceLink & { + sys: ResourceSys; +}; + +export type WorkingGroupsDescriptionResourcesInline = ResourceLink & { + sys: ResourceSys; }; export type WorkingGroupsFilter = { @@ -10137,6 +10259,11 @@ export type NewsContentDataFragment = Pick< > & { sys: Pick; thumbnail?: Maybe>; + tagsCollection?: Maybe< + Pick & { + items: Array & { sys: Pick }>>; + } + >; }; export type FetchNewsByIdQueryVariables = Exact<{ @@ -10151,6 +10278,11 @@ export type FetchNewsByIdQuery = { > & { sys: Pick; thumbnail?: Maybe>; + tagsCollection?: Maybe< + Pick & { + items: Array & { sys: Pick }>>; + } + >; } >; }; @@ -10173,6 +10305,13 @@ export type FetchNewsQuery = { > & { sys: Pick; thumbnail?: Maybe>; + tagsCollection?: Maybe< + Pick & { + items: Array< + Maybe & { sys: Pick }> + >; + } + >; } > >; @@ -14695,6 +14834,46 @@ export const NewsContentDataFragmentDoc = { ], }, }, + { + kind: 'Field', + name: { kind: 'Name', value: 'tagsCollection' }, + arguments: [ + { + kind: 'Argument', + name: { kind: 'Name', value: 'limit' }, + value: { kind: 'IntValue', value: '20' }, + }, + ], + selectionSet: { + kind: 'SelectionSet', + selections: [ + { kind: 'Field', name: { kind: 'Name', value: 'total' } }, + { + kind: 'Field', + name: { kind: 'Name', value: 'items' }, + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'sys' }, + selectionSet: { + kind: 'SelectionSet', + selections: [ + { + kind: 'Field', + name: { kind: 'Name', value: 'id' }, + }, + ], + }, + }, + { kind: 'Field', name: { kind: 'Name', value: 'name' } }, + ], + }, + }, + ], + }, + }, { kind: 'Field', name: { kind: 'Name', value: 'link' } }, { kind: 'Field', name: { kind: 'Name', value: 'linkText' } }, { kind: 'Field', name: { kind: 'Name', value: 'publishDate' } }, diff --git a/packages/contentful/src/gp2/queries/news.queries.ts b/packages/contentful/src/gp2/queries/news.queries.ts index 91b9f0497e..703903f838 100644 --- a/packages/contentful/src/gp2/queries/news.queries.ts +++ b/packages/contentful/src/gp2/queries/news.queries.ts @@ -12,6 +12,15 @@ export const newsContentQueryFragment = gql` thumbnail { url } + tagsCollection(limit: 20) { + total + items { + sys { + id + } + name + } + } link linkText publishDate diff --git a/packages/contentful/src/gp2/schema/autogenerated-schema.graphql b/packages/contentful/src/gp2/schema/autogenerated-schema.graphql index a11fd7d711..c8a89a67de 100644 --- a/packages/contentful/src/gp2/schema/autogenerated-schema.graphql +++ b/packages/contentful/src/gp2/schema/autogenerated-schema.graphql @@ -1133,9 +1133,21 @@ type EventsNotesLinks { } type EventsNotesResources { - block: [ResourceLink!]! - hyperlink: [ResourceLink!]! - inline: [ResourceLink!]! + block: [EventsNotesResourcesBlock!]! + hyperlink: [EventsNotesResourcesHyperlink!]! + inline: [EventsNotesResourcesInline!]! +} + +type EventsNotesResourcesBlock implements ResourceLink { + sys: ResourceSys! +} + +type EventsNotesResourcesHyperlink implements ResourceLink { + sys: ResourceSys! +} + +type EventsNotesResourcesInline implements ResourceLink { + sys: ResourceSys! } enum EventsOrder { @@ -1210,9 +1222,21 @@ type EventsPresentationLinks { } type EventsPresentationResources { - block: [ResourceLink!]! - hyperlink: [ResourceLink!]! - inline: [ResourceLink!]! + block: [EventsPresentationResourcesBlock!]! + hyperlink: [EventsPresentationResourcesHyperlink!]! + inline: [EventsPresentationResourcesInline!]! +} + +type EventsPresentationResourcesBlock implements ResourceLink { + sys: ResourceSys! +} + +type EventsPresentationResourcesHyperlink implements ResourceLink { + sys: ResourceSys! +} + +type EventsPresentationResourcesInline implements ResourceLink { + sys: ResourceSys! } type EventsSpeakersCollection { @@ -1278,9 +1302,21 @@ type EventsVideoRecordingLinks { } type EventsVideoRecordingResources { - block: [ResourceLink!]! - hyperlink: [ResourceLink!]! - inline: [ResourceLink!]! + block: [EventsVideoRecordingResourcesBlock!]! + hyperlink: [EventsVideoRecordingResourcesHyperlink!]! + inline: [EventsVideoRecordingResourcesInline!]! +} + +type EventsVideoRecordingResourcesBlock implements ResourceLink { + sys: ResourceSys! +} + +type EventsVideoRecordingResourcesHyperlink implements ResourceLink { + sys: ResourceSys! +} + +type EventsVideoRecordingResourcesInline implements ResourceLink { + sys: ResourceSys! } """[See type definition](https://app.contentful.com/spaces/6ekgyp1432o9/content_types/externalUsers)""" @@ -1983,6 +2019,7 @@ type News implements Entry { publishDate(locale: String): DateTime shortText(locale: String): String sys: Sys! + tagsCollection(limit: Int = 100, locale: String, order: [NewsTagsCollectionOrder], preview: Boolean, skip: Int = 0, where: TagsFilter): NewsTagsCollection thumbnail(locale: String, preview: Boolean): Asset title(locale: String): String type(locale: String): String @@ -2030,6 +2067,8 @@ input NewsFilter { shortText_not_contains: String shortText_not_in: [String] sys: SysFilter + tags: cfTagsNestedFilter + tagsCollection_exists: Boolean thumbnail_exists: Boolean title: String title_contains: String @@ -2070,6 +2109,26 @@ enum NewsOrder { type_DESC } +type NewsTagsCollection { + items: [Tags]! + limit: Int! + skip: Int! + total: Int! +} + +enum NewsTagsCollectionOrder { + name_ASC + name_DESC + sys_firstPublishedAt_ASC + sys_firstPublishedAt_DESC + sys_id_ASC + sys_id_DESC + sys_publishedAt_ASC + sys_publishedAt_DESC + sys_publishedVersion_ASC + sys_publishedVersion_DESC +} + """[See type definition](https://app.contentful.com/spaces/6ekgyp1432o9/content_types/outputVersion)""" type OutputVersion implements Entry { addedDate(locale: String): DateTime @@ -2786,9 +2845,21 @@ type PagesTextLinks { } type PagesTextResources { - block: [ResourceLink!]! - hyperlink: [ResourceLink!]! - inline: [ResourceLink!]! + block: [PagesTextResourcesBlock!]! + hyperlink: [PagesTextResourcesHyperlink!]! + inline: [PagesTextResourcesInline!]! +} + +type PagesTextResourcesBlock implements ResourceLink { + sys: ResourceSys! +} + +type PagesTextResourcesHyperlink implements ResourceLink { + sys: ResourceSys! +} + +type PagesTextResourcesInline implements ResourceLink { + sys: ResourceSys! } """[See type definition](https://app.contentful.com/spaces/6ekgyp1432o9/content_types/projectMembership)""" @@ -3245,13 +3316,12 @@ type Query { workingGroupsCollection(limit: Int = 100, locale: String, order: [WorkingGroupsOrder], preview: Boolean, skip: Int = 0, where: WorkingGroupsFilter): WorkingGroupsCollection } -type ResourceLink { +interface ResourceLink { sys: ResourceSys! } type ResourceSys { linkType: String! - type: String! urn: String! } @@ -3467,6 +3537,7 @@ input TagsFilter { type TagsLinkingCollections { entryCollection(limit: Int = 100, locale: String, preview: Boolean, skip: Int = 0): EntryCollection eventsCollection(limit: Int = 100, locale: String, order: [TagsLinkingCollectionsEventsCollectionOrder], preview: Boolean, skip: Int = 0): EventsCollection + newsCollection(limit: Int = 100, locale: String, order: [TagsLinkingCollectionsNewsCollectionOrder], preview: Boolean, skip: Int = 0): NewsCollection outputsCollection(limit: Int = 100, locale: String, order: [TagsLinkingCollectionsOutputsCollectionOrder], preview: Boolean, skip: Int = 0): OutputsCollection projectsCollection(limit: Int = 100, locale: String, order: [TagsLinkingCollectionsProjectsCollectionOrder], preview: Boolean, skip: Int = 0): ProjectsCollection usersCollection(limit: Int = 100, locale: String, order: [TagsLinkingCollectionsUsersCollectionOrder], preview: Boolean, skip: Int = 0): UsersCollection @@ -3522,6 +3593,25 @@ enum TagsLinkingCollectionsEventsCollectionOrder { videoRecordingUpdatedAt_DESC } +enum TagsLinkingCollectionsNewsCollectionOrder { + linkText_ASC + linkText_DESC + link_ASC + link_DESC + publishDate_ASC + publishDate_DESC + sys_firstPublishedAt_ASC + sys_firstPublishedAt_DESC + sys_id_ASC + sys_id_DESC + sys_publishedAt_ASC + sys_publishedAt_DESC + sys_publishedVersion_ASC + sys_publishedVersion_DESC + type_ASC + type_DESC +} + enum TagsLinkingCollectionsOutputsCollectionOrder { accessionNumber_ASC accessionNumber_DESC @@ -4473,9 +4563,21 @@ type WorkingGroupsDescriptionLinks { } type WorkingGroupsDescriptionResources { - block: [ResourceLink!]! - hyperlink: [ResourceLink!]! - inline: [ResourceLink!]! + block: [WorkingGroupsDescriptionResourcesBlock!]! + hyperlink: [WorkingGroupsDescriptionResourcesHyperlink!]! + inline: [WorkingGroupsDescriptionResourcesInline!]! +} + +type WorkingGroupsDescriptionResourcesBlock implements ResourceLink { + sys: ResourceSys! +} + +type WorkingGroupsDescriptionResourcesHyperlink implements ResourceLink { + sys: ResourceSys! +} + +type WorkingGroupsDescriptionResourcesInline implements ResourceLink { + sys: ResourceSys! } input WorkingGroupsFilter { diff --git a/packages/fixtures/src/gp2/news.ts b/packages/fixtures/src/gp2/news.ts index 05345f66f1..c967917eba 100644 --- a/packages/fixtures/src/gp2/news.ts +++ b/packages/fixtures/src/gp2/news.ts @@ -8,6 +8,7 @@ export const mockedNews: gp2.NewsResponse = { shortText: 'this is another news item', title: 'Another news', type: 'news', + tags: [], }; export const createNewsResponse = ( @@ -32,6 +33,8 @@ export const newsResponseTemplate = ({ shortText: `${key} short text`, type: 'news', created: new Date().toISOString(), + tags: [], + thumbnail: 'https://image.com', }); export const createListNewsResponse = ( diff --git a/packages/gp2-components/src/index.ts b/packages/gp2-components/src/index.ts index 22cef6d853..886daf3e82 100644 --- a/packages/gp2-components/src/index.ts +++ b/packages/gp2-components/src/index.ts @@ -13,7 +13,6 @@ export { HeaderLogo, IconWithLabel, NavigationLink, - NewsItem, StatusPill, UserCardInfo, UserMenu, diff --git a/packages/gp2-components/src/molecules/NewsItem.tsx b/packages/gp2-components/src/molecules/NewsItem.tsx deleted file mode 100644 index 6130df318b..0000000000 --- a/packages/gp2-components/src/molecules/NewsItem.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { gp2 } from '@asap-hub/model'; -import { - newsPlaceholder, - Subtitle, - ExternalLink, - Paragraph, - Caption, - formatDate, - pixels, - Card, - Ellipsis, -} from '@asap-hub/react-components'; -import { css } from '@emotion/react'; -import { mobileQuery } from '../layout'; -import colors from '../templates/colors'; - -const { rem } = pixels; - -const newsItemStyles = css({ - display: 'grid', - grid: ` - "image headline link" 32px - "image content content" auto - /192px auto min-content - `, - columnGap: rem(24), - rowGap: rem(24), - margin: `${rem(32)} ${rem(24)}`, - [mobileQuery]: { - grid: ` - "headline link" min-content - "content content" auto - / auto min-content - `, - }, -}); - -const imageContainerStyles = css({ - flexShrink: 0, - borderRadius: '8px', - height: rem(192), - width: rem(192), - overflow: 'hidden', - - gridArea: 'image', - svg: { borderRadius: '8px' }, - [mobileQuery]: { display: 'none' }, -}); - -const imageStyles = css({ - objectFit: 'cover', - width: '100%', - height: '100%', -}); - -const contentStyles = css({ - gridArea: 'content', - color: colors.neutral900.rgb, -}); - -type NewsItemProps = gp2.NewsResponse; - -const NewsItem: React.FC = ({ - title, - thumbnail, - shortText, - created, - linkText, - link, -}) => ( - -
-
- {thumbnail ? ( - {`"${title}"'s - ) : ( - newsPlaceholder - )} -
-
- - {title} - -
- {link && ( -
- -
- )} -
- - {shortText} - - - Posted: {formatDate(new Date(created))} by ASAP - -
-
-
-); - -export default NewsItem; diff --git a/packages/gp2-components/src/molecules/__tests__/NewsItem.test.tsx b/packages/gp2-components/src/molecules/__tests__/NewsItem.test.tsx deleted file mode 100644 index 74baa204bb..0000000000 --- a/packages/gp2-components/src/molecules/__tests__/NewsItem.test.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import NewsItem from '../NewsItem'; - -describe('NewsItem', () => { - const newsProps = { - title: 'News Title', - shortText: 'News short text', - thumbnail: 'http://image.com/assets/thumbnail-uuid1', - created: '2021-01-01T00:00:00.000Z', - linkText: 'News link text', - link: 'https://www.google.com', - id: '1', - type: 'news' as const, - }; - - it('renders correctly', () => { - render(); - expect( - screen.getByRole('heading', { name: 'News Title' }), - ).toBeInTheDocument(); - expect(screen.getByText('News short text')).toBeInTheDocument(); - expect(screen.getByText('News link text')).toBeInTheDocument(); - expect( - screen.getByRole('img', { - name: /"News Title"'s thumbnail/i, - }), - ).toBeInTheDocument(); - expect( - screen.getByRole('link', { name: /News link text/i }), - ).toHaveAttribute('href', 'https://www.google.com'); - }); - it('doesnt render link if link is not provided', () => { - render(); - expect(screen.queryByText('News link text')).not.toBeInTheDocument(); - }); -}); diff --git a/packages/gp2-components/src/molecules/index.ts b/packages/gp2-components/src/molecules/index.ts index 89e6f36c64..008ba16761 100644 --- a/packages/gp2-components/src/molecules/index.ts +++ b/packages/gp2-components/src/molecules/index.ts @@ -11,7 +11,6 @@ export { default as ExpandableText } from './ExpandableText'; export { default as HeaderLogo } from './HeaderLogo'; export { default as IconWithLabel } from './IconWithLabel'; export { default as NavigationLink } from './NavigationLink'; -export { default as NewsItem } from './NewsItem'; export { default as SocialIcons } from './SocialIcons'; export { default as StatusPill } from './StatusPill'; export { default as UserCardInfo } from './UserCardInfo'; diff --git a/packages/gp2-components/src/templates/DashboardPageBody.tsx b/packages/gp2-components/src/templates/DashboardPageBody.tsx index 359cfb2413..b25e5f17f1 100644 --- a/packages/gp2-components/src/templates/DashboardPageBody.tsx +++ b/packages/gp2-components/src/templates/DashboardPageBody.tsx @@ -16,6 +16,7 @@ import { RemindersCard, PastEventsDashboardCard, RecentSharedOutputs, + NewsCard, } from '@asap-hub/react-components'; import { css } from '@emotion/react'; import { useFlags } from '@asap-hub/react-context'; @@ -24,7 +25,6 @@ import { useHistory } from 'react-router-dom'; import { ArticleIcon } from '../icons'; import { mobileQuery } from '../layout'; import GuideDescription from '../molecules/GuideDescription'; -import { NewsItem } from '../molecules'; import InfoCard from '../molecules/InfoCard'; import { DashboardUserCard } from '../organisms'; import { getIconForDocumentType } from '../utils'; @@ -289,7 +289,7 @@ const DashboardPageBody: React.FC = ({ Here is the latest GP2 news.
- + View All diff --git a/packages/gp2-components/src/templates/TagSearchPageList.tsx b/packages/gp2-components/src/templates/TagSearchPageList.tsx index e8eae457d8..04523c6c51 100644 --- a/packages/gp2-components/src/templates/TagSearchPageList.tsx +++ b/packages/gp2-components/src/templates/TagSearchPageList.tsx @@ -35,6 +35,7 @@ const outputFilters: ReadonlyArray | Title> = [ { value: 'event', label: 'Events' }, { value: 'user', label: 'People' }, { value: 'project', label: 'Projects' }, + { value: 'news', label: 'News' }, ]; const styles = css({ diff --git a/packages/model/src/gp2/news.ts b/packages/model/src/gp2/news.ts index 17fe4aa596..7185f4f8e5 100644 --- a/packages/model/src/gp2/news.ts +++ b/packages/model/src/gp2/news.ts @@ -13,6 +13,7 @@ export type NewsDataObject = { link?: string; linkText?: string; type: NewsType; + tags: string[]; }; export type ListNewsDataObject = ListResponse; diff --git a/packages/react-components/src/organisms/NewsCard.tsx b/packages/react-components/src/organisms/NewsCard.tsx index ab26a4cccd..c9c52dac74 100644 --- a/packages/react-components/src/organisms/NewsCard.tsx +++ b/packages/react-components/src/organisms/NewsCard.tsx @@ -2,8 +2,8 @@ import { css } from '@emotion/react'; import { NewsResponse, NewsType, TutorialsResponse } from '@asap-hub/model'; import { news, discover } from '@asap-hub/routing'; -import { Card, Paragraph, Headline4 } from '../atoms'; -import { perRem, smallDesktopScreen } from '../pixels'; +import { Card, Headline4, Ellipsis } from '../atoms'; +import { rem, smallDesktopScreen } from '../pixels'; import { formatDate } from '../date'; import { newsPlaceholder, trainingPlaceholderIcon } from '../icons'; import { ExternalLink, LinkHeadline, ImageLink } from '../molecules'; @@ -18,13 +18,11 @@ const imageStyle = css({ const imageContainerStyle = css({ flexShrink: 0, - borderRadius: `${6 / perRem}em`, - height: `${184 / perRem}em`, - marginTop: `${9 / perRem}em`, - marginBottom: `${3 / perRem}em`, + borderRadius: rem(8), + marginRight: rem(24), - marginRight: `${24 / perRem}em`, - width: `${184 / perRem}em`, + height: rem(192), + width: rem(192), overflow: 'hidden', [`@media (max-width: ${smallDesktopScreen.min}px)`]: { @@ -35,18 +33,29 @@ const imageContainerStyle = css({ const headerStyles = css({ display: 'flex', justifyContent: 'space-between', + alignItems: 'baseline', +}); + +const titleStyles = css({ + paddingRight: rem(15), }); const cardStyle = css({ display: 'flex', flexDirection: 'row', - marginBottom: `${6 / perRem}em`, + padding: `${rem(32)} ${rem(24)}`, }); const containerStyle = css({ flex: 1, display: 'flex', flexDirection: 'column', + gap: rem(24), +}); + +const shortTextStyles = css({ + flex: 1, + color: lead.rgb, }); const footerStyles = css({ @@ -55,10 +64,6 @@ const footerStyles = css({ justifySelf: 'flex-end', }); -const tagListStyle = css({ - margin: `${12 / perRem}em 0`, -}); - const placeholders: Record = { News: newsPlaceholder, Tutorial: trainingPlaceholderIcon, @@ -83,11 +88,11 @@ const NewsCard: React.FC< ? discover({}).tutorials({}).tutorial({ tutorialId: id }).$ : news({}).article({ articleId: id }).$; const titleComponent = text ? ( - + {title} ) : ( - {title} + {title} ); const newsLink = text && href; @@ -106,7 +111,7 @@ const NewsCard: React.FC< ); return ( - +
{newsLink ? ( @@ -117,19 +122,13 @@ const NewsCard: React.FC<
-
- {titleComponent} -
+
{titleComponent}
{link ? : null}
-
- {shortText} +
+ {shortText}
- {!!tags.length && ( -
- -
- )} + {!!tags.length && } Posted: {formatDate(new Date(created))} by ASAP