diff --git a/src/components.d.ts b/src/components.d.ts index 208b883686b202183d3aa557d83b2d0008cc3c4d..04cf52eca573579c12437ae5579d335848971b6a 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -26,6 +26,7 @@ import { KeyHighlightVariants, LinkBoxVariants, LinkVariants, + ListItemVariants, MenuType, PaginationItemVariants, PersonCardVariants, @@ -46,6 +47,7 @@ import {ProcessFlowValue} from './components/hy-large-process-flow/hy-large-proc import {PhaseValue} from './components/hy-large-process-flow-phase/hy-large-process-flow-phase'; import {LinkBox} from './components/link-box-list/link-box-list'; import {CtaLinkValue} from './components/hy-link-list/hy-link-list'; +import {AdditionalInfo, RelatedLink} from './components/hy-list-item/hy-list-item'; import {MenuLanguage} from './components/navigation/menu-language/menu-language'; import {ComponentLabels} from './components/site-header/site-header'; import {ProcessFlowBoxValue} from './components/process/process'; @@ -456,12 +458,21 @@ export namespace Components { listHeading: string; } interface HyListItem { + additionalInfo?: AdditionalInfo[] | string; + headerstyle: string; + imageLabel?: string; isExternal: boolean; itemDescription?: string; + itemImageAlt?: string; + itemImageUrl?: string; itemTitle?: string; itemType?: string; + outGoingUrlLabel?: string; + relatedLinks?: RelatedLink[] | string; + relatedLinksBlockTitle?: string; scLabel?: string; url?: string; + variant: ListItemVariants; } interface HyMain { hasSidebar: boolean; @@ -1693,12 +1704,21 @@ declare namespace LocalJSX { listHeading?: string; } interface HyListItem { + additionalInfo?: AdditionalInfo[] | string; + headerstyle?: string; + imageLabel?: string; isExternal?: boolean; itemDescription?: string; + itemImageAlt?: string; + itemImageUrl?: string; itemTitle?: string; itemType?: string; + outGoingUrlLabel?: string; + relatedLinks?: RelatedLink[] | string; + relatedLinksBlockTitle?: string; scLabel?: string; url?: string; + variant?: ListItemVariants; } interface HyMain { hasSidebar?: boolean; diff --git a/src/components/hy-list-item/hy-list-item.scss b/src/components/hy-list-item/hy-list-item.scss index f67c979296f11da075fe193ba4140dd68fa8105f..f14818888ba75b029ecf18672688134d272305c5 100644 --- a/src/components/hy-list-item/hy-list-item.scss +++ b/src/components/hy-list-item/hy-list-item.scss @@ -5,79 +5,457 @@ .hy-list-item { display: flex; font-family: var(--main-font-family); - margin-bottom: 28px; text-decoration: none; - @include breakpoint($wide) { - margin-bottom: 32px; - } + &__default { + margin-bottom: 28px; + @include breakpoint($wide) { + margin-bottom: 32px; + } - &__info-container { - &__header { - @include font-size(12px, 14px); - color: var(--grayscale-dark); - letter-spacing: 0; - margin-bottom: 4px; + &__info-container { + &__header { + @include font-size(12px, 14px); + color: var(--grayscale-dark); + letter-spacing: 0; + margin-bottom: 4px; - @include breakpoint($narrow) { - @include font-size(14px, 18px); - margin-bottom: 2px; + @include breakpoint($narrow) { + @include font-size(14px, 18px); + margin-bottom: 2px; + } + } + + &__title { + @include font-size(18px, 22px); + @include font-weight($bold); + color: var(--brand-main-light); + letter-spacing: -0.56px; + margin-bottom: 6px; + + @include breakpoint($narrow) { + @include font-size(22px, 28px); + letter-spacing: -0.69px; + margin-bottom: 10px; + } + @include breakpoint($xlarge) { + @include font-size(26px, 32px); + letter-spacing: -0.8px; + margin-bottom: 8px; + } + } + + &__link-container { + align-items: center; + display: flex; + margin-bottom: 6px; + + @include breakpoint($narrow) { + margin-bottom: 8px; + } } + + &__link { + @include font-size(12px, 16px); + color: var(--grayscale-dark); + letter-spacing: -0.07px; + margin-left: 4px; + + @include breakpoint($narrow) { + margin-left: 5px; + } + + &__icon { + svg { + fill: var(--grayscale-dark); + } + } + } + + &__description { + @include font-size(14px, 20px); + color: var(--grayscale-black); + letter-spacing: 0; + + @include breakpoint($narrow) { + @include font-size(16px, 24px); + } + } + } + } + + // Degree programmes. + &__degree { + background: radial-gradient(circle, var(--grayscale-light) 0%, var(--grayscale-background-box) 100%); + flex-direction: column; + @include breakpoint($narrow) { + flex-direction: row; } - &__title { - @include font-size(18px, 22px); - @include font-weight($bold); - color: var(--brand-main-light); - letter-spacing: -0.56px; - margin-bottom: 6px; + &__image-container { + display: block; + min-width: 100%; + position: relative; + width: 100%; @include breakpoint($narrow) { - @include font-size(22px, 28px); - letter-spacing: -0.69px; - margin-bottom: 10px; + min-width: 24%; + width: 24%; } - @include breakpoint($fullhd) { - @include font-size(26px, 32px); - letter-spacing: -0.8px; - margin-bottom: 8px; + + &__image { + vertical-align: bottom; + max-width: 100%; + height: 100%; + + img { + object-fit: cover; + width: 100%; + height: 100%; + } + } + + span { + @include font-weight($semibold); + @include font-size(12px, 14px); + background-color: var(--additional-yellow); + box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.05); + color: var(--grayscale-black); + left: -4px; + letter-spacing: -0.07px; + padding: 8px 5px; + position: absolute; + top: 16px; + text-transform: uppercase; + + @include breakpoint($narrow) { + left: -8px; + } + @include breakpoint($wide) { + top: 22px; + } } } - &__link-container { - align-items: center; + &__info-container { display: flex; - margin-bottom: 6px; + flex-direction: column; + margin: 0; + padding: 12px; + position: relative; - @include breakpoint($narrow) { + // 960-1600 with Sidebar; + @include breakpoint($wide) { + padding: 12px 16px; + } + // >1600 with sidebar same as >1200 without sidebar + @include breakpoint($xlarge) { + padding: 20px 24px; + } + + &__data { + display: flex; + flex-direction: column; + margin-bottom: 32px; + } + + // >1200 no sidebar; + &__large { + @include breakpoint($extrawide) { + padding: 20px 24px; + } + } + + &__title { + @include font-size(18px, 22px); + @include font-weight($bold); + color: var(--link-blue); + letter-spacing: -0.54px; margin-bottom: 8px; + + // 960-1600 with Sidebar; + @include breakpoint($wide) { + @include font-size(18px, 24px); + letter-spacing: -0.5px; + } + + // >1600 with sidebar same as >1200 without sidebar + @include breakpoint($xlarge) { + @include font-size(20px, 26px); + letter-spacing: -0.6px; + } + + &__large { + // >1200 no sidebar; + @include breakpoint($extrawide) { + @include font-size(20px, 26px); + letter-spacing: -0.6px; + } + } } - } - &__link { - @include font-size(12px, 16px); - color: var(--grayscale-dark); - letter-spacing: -0.07px; - margin-left: 4px; + &__description { + @include font-size(14px, 18px); + color: var(--grayscale-dark); + letter-spacing: 0; + margin-bottom: 16px; - @include breakpoint($narrow) { - margin-left: 5px; + @include breakpoint($narrow) { + margin-bottom: 12px; + } + // 960-1600 with Sidebar; + @include breakpoint($wide) { + letter-spacing: -0.2; + } + // >1600 with sidebar same as >1200 without sidebar + @include breakpoint($xlarge) { + @include font-size(16px, 20px); + letter-spacing: -0.25px; + } + + &__large { + // >1200 no sidebar; + @include breakpoint($extrawide) { + @include font-size(16px, 20px); + letter-spacing: -0.25px; + } + } } - &__icon { - svg { - fill: var(--grayscale-dark); + &__additional-info { + &__item { + @include font-size(12px, 16px); + @include font-weight($light); + align-items: center; + color: var(--grayscale-black); + display: flex; + flex-direction: row; + letter-spacing: -0.2px; + margin-bottom: 4px; + + @include breakpoint($xlarge) { + @include font-size(14px, 16px); + } + // >1200 no sidebar; + &__large { + @include breakpoint($extrawide) { + @include font-size(14px, 16px); + } + } + } + &__item.active::before { + content: ' '; + box-sizing: border-box; + height: 14px; + width: 14px; + border: 3px solid #ffffff; + border-radius: 4px; + background-color: #f9a21a; + left: -7px; + position: absolute; + + @include breakpoint($narrow) { + border: none; + border-radius: 4px; + height: 8px; + margin-right: 6px; + width: 8px; + left: 0; + position: relative; + } + } + } + + &__outgoing-link { + display: flex; + flex-direction: row; + justify-content: flex-end; + padding-top: 16px; + + a { + align-items: center; + bottom: 12px; + display: flex; + flex-direction: row; + position: absolute; + right: -6px; + text-decoration: none; + + .label { + @include font-size(14px, 16px); + @include font-weight($bold); + color: var(--link-blue); + letter-spacing: -0.5px; + margin-right: 8px; + text-align: right; + } + + .icon-wrapper { + background-color: var(--brand-main-light); + z-index: 10; + + .icon { + position: relative; + overflow: hidden; + + svg { + background-color: transparent; + box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1); + fill: var(--grayscale-white); + padding: 6.4px; + + @include breakpoint($xlarge) { + height: 36px; + width: 36px; + padding: 7.2px; + } + } + } + } + } + + // >1200 no sidebar; + &__large { + svg { + @include breakpoint($extrawide) { + height: 36px; + width: 36px; + padding: 7.2px; + } + } } } } + } +} - &__description { - @include font-size(14px, 20px); - color: var(--grayscale-black); - letter-spacing: 0; +.hy-list-item-wrapper { + font-family: var(--main-font-family); + + &__degree { + margin-bottom: 16px; + @include breakpoint($narrow) { + flex-direction: row; + margin-bottom: 24px; + } + @include breakpoint($wide) { + margin-bottom: 28px; + } + @include breakpoint($xlarge) { + margin-bottom: 32px; + } + // >1200 no sidebar; + &__large { + @include breakpoint($extrawide) { + margin-bottom: 32px; + } + } + + &__related-info { + background: radial-gradient(circle, #f8f8f8 0%, #f5f5f5 100%); + margin-top: 4px; + padding: 0 12px 0 16px; @include breakpoint($narrow) { - @include font-size(16px, 24px); + padding: 0 24px 0 16px; + } + @include breakpoint($wide) { + margin-top: 6px; + padding: 0 24px 0 24px; + } + @include breakpoint($xlarge) { + margin-top: 8px; + } + // >1200 no sidebar; + &__large { + @include breakpoint($extrawide) { + margin-top: 8px; + } + } + + &__heading { + &__link { + text-decoration: none; + } + + &__button { + @include font-size(12px, 16px); + align-items: center; + background: radial-gradient(circle, #f8f8f8 0%, #f5f5f5 100%); + border: none; + color: var(--grayscale-black); + display: flex; + flex-direction: row; + font-family: var(--main-font-family); + letter-spacing: -0.2px; + height: 100%; + padding: 8px 0; + width: 100%; + + @include breakpoint($narrow) { + @include font-size(12px, 20px); + padding: 14px 0; + } + @include breakpoint($wide) { + @include font-size(14px, 20px); + letter-spacing: -0.4px; + padding: 18px 0; + } + + &:hover { + cursor: pointer; + } + + &__is-open { + svg { + transform: rotate(180deg); + } + } + } + + &__icon { + margin-right: 0.5rem; + z-index: 0; + + @include breakpoint($wide) { + margin-right: 12px; + } + + svg { + fill: var(--brand-main); + height: 14px; + width: 14px; + + @include breakpoint($wide) { + height: 18px; + width: 18px; + } + } + } + } + + &__content { + display: none; + + &__is-open { + border-top: 1px solid #d2d2d2; + display: flex; + flex-direction: column; + padding: 20px 8px 5px 8px; + + @include breakpoint($wide) { + padding: 20px 0 14px 0; + } + } + } + + &__link { + @include font-size(14px, 16px); + @include font-weight($bold); + color: var(--link-blue); + letter-spacing: -0.4px; + margin-bottom: 16px; + text-decoration: none; } } } diff --git a/src/components/hy-list-item/hy-list-item.tsx b/src/components/hy-list-item/hy-list-item.tsx index f06fdd00935c525749325c40054d9a3b3e4e0a2f..337e777e24c320a00b7c58395cf77dfd8226ead5 100644 --- a/src/components/hy-list-item/hy-list-item.tsx +++ b/src/components/hy-list-item/hy-list-item.tsx @@ -1,4 +1,22 @@ -import {Component, h, Prop} from '@stencil/core'; +export interface RelatedLink { + label: string; + url: string; + isExternal: boolean; + scLabel: string; +} + +export interface AdditionalInfo { + text: string; + isActive: boolean; +} + +import {Component, h, Prop, Watch, State, Listen, Element} from '@stencil/core'; +import {ListItemVariants} from '../../utils/utils'; + +let keys = { + enter: 'Enter', + space: 'Space', +}; @Component({ tag: 'hy-list-item', @@ -6,37 +24,322 @@ import {Component, h, Prop} from '@stencil/core'; shadow: false, }) export class HyListItem { + @Element() el: HTMLElement; + @State() isExpandedRelatedContent: boolean = false; + @Prop() variant: ListItemVariants = ListItemVariants.default; @Prop() itemType?: string; @Prop() itemTitle?: string; @Prop() itemDescription?: string; @Prop() url?: string; @Prop() isExternal: boolean = false; @Prop() scLabel?: string; + @Prop() itemImageUrl?: string; + @Prop() itemImageAlt?: string; + @Prop() relatedLinksBlockTitle?: string; + private _relatedLinks: RelatedLink[]; + @Prop() relatedLinks?: RelatedLink[] | string; + @Prop() imageLabel?: string; + @Prop() outGoingUrlLabel?: string = 'Go to programme page'; + private _additionalInfo: AdditionalInfo[]; + @Prop() additionalInfo?: AdditionalInfo[] | string; + @Prop() headerstyle: string = 'common'; + + @State() _imageMinHeight: number = 0; + @State() _imageMinWidth: number = 0; + + @Watch('relatedLinks') + relatedLinksWatcher(newValue: RelatedLink[] | string) { + if (typeof newValue === 'string') { + this._relatedLinks = JSON.parse(newValue); + } else { + this._relatedLinks = newValue; + } + } + + @Watch('additionalInfo') + additionalInfoWatcher(newValue: AdditionalInfo[] | string) { + if (typeof newValue === 'string') { + this._additionalInfo = JSON.parse(newValue); + } else { + this._additionalInfo = newValue; + } + } + + expandRelatedInfoPanel(target) { + let contentSelector = `hy-list-item-wrapper__${this.variant}__related-info__content`; + let targetButtonClass = 'hy-list-item-wrapper__degree__related-info__heading__button'; + let contentSection = document.querySelectorAll(`[aria-labelledBy="${target.id}"]`)[0] as HTMLElement; + if (contentSection) { + if (!this.isExpandedRelatedContent) { + contentSection.classList.add(`${contentSelector}__is-open`); + target.classList.add(`${targetButtonClass}__is-open`); + } else { + contentSection.classList.remove(`${contentSelector}__is-open`); + target.classList.remove(`${targetButtonClass}__is-open`); + } + this.isExpandedRelatedContent = !this.isExpandedRelatedContent; + } + } + + @Listen('keydown') + handleKeyDown(event: KeyboardEvent) { + const key = event.code; + let target = event.target as HTMLButtonElement; + + const targetElement = target.tagName.toLowerCase(); + const possibleTags = [targetElement].some((r) => ['svg', 'path', 'button', 'a'].indexOf(r) >= 0); + + if ( + target && + possibleTags && + !target.classList.contains('hy-list-item-wrapper__degree__related-info__link') && + [keys.enter, keys.space].includes(key) + ) { + if (targetElement !== 'button') { + target = target.closest(`.hy-list-item-wrapper__${this.variant}__related-info__heading__button`); + } + this.expandRelatedInfoPanel(target); + event.preventDefault(); + } + } + + @Listen('click') + handleClick(event) { + let target = event.target as HTMLElement; + const targetElement = target.tagName.toLowerCase(); + const possibleTags = [targetElement].some((r) => ['svg', 'path', 'button', 'a'].indexOf(r) >= 0); + + if (target && possibleTags && !target.classList.contains('hy-list-item-wrapper__degree__related-info__link')) { + if (targetElement !== 'button') { + target = target.closest(`.hy-list-item-wrapper__${this.variant}__related-info__heading__button`); + } + this.expandRelatedInfoPanel(target); + event.preventDefault(); + event.stopImmediatePropagation(); + } + } + + componentWillLoad() { + this.relatedLinksWatcher(this.relatedLinks); + this.additionalInfoWatcher(this.additionalInfo); + } + + componentDidLoad() { + let hyMainDiv = this.el.closest('.hy-main'); + if (hyMainDiv) { + if (!hyMainDiv.classList.contains('with-sidebar')) { + this.headerstyle = 'large'; + } + } + if (this.variant == ListItemVariants.degreeProgramme) { + const image = document.querySelector('.hy-list-item__degree__image-container__image img') as any; + + if (image) { + image.addEventListener('load', function () { + const outputImageAspectRatio = 1; + const imageHeight = image.naturalHeight; + const imageWidth = image.naturalWidth; + const inputImageAspectRatio = imageWidth / imageHeight; + this._imageMinWidth = imageWidth; + this._imageMinHeight = imageHeight; + if (inputImageAspectRatio > outputImageAspectRatio) { + this._imageMinWidth = imageHeight * outputImageAspectRatio; + } else if (inputImageAspectRatio < outputImageAspectRatio) { + this._imageMinHeight = imageWidth / outputImageAspectRatio; + } + }); + } + } + } render() { - const classAttributes = ['hy-list-item'].join(' '); + const classWrapperAttributes = [ + 'hy-list-item-wrapper', + `hy-list-item-wrapper__${this.variant}`, + `hy-list-item-wrapper__${this.variant}__${this.headerstyle}`, + ].join(' '); + const classAttributes = ['hy-list-item', `hy-list-item__${this.variant}`].join(' '); + const classInfoContainerAttributes = [ + `hy-list-item__${this.variant}__info-container`, + `hy-list-item__${this.variant}__info-container__${this.headerstyle}`, + ].join(' '); + const classInfoContainerDataAttributes = [ + `hy-list-item__${this.variant}__info-container__data`, + `hy-list-item__${this.variant}__info-container__data__${this.headerstyle}`, + ].join(' '); + const classInfoContainerHeader = [ + `hy-list-item__${this.variant}__info-container__header`, + `hy-list-item__${this.variant}__info-container__header__${this.headerstyle}`, + ].join(' '); + const classInfoContainerTitle = [ + `hy-list-item__${this.variant}__info-container__title`, + `hy-list-item__${this.variant}__info-container__title__${this.headerstyle}`, + ].join(' '); + const classInfoContainerDescription = [ + `hy-list-item__${this.variant}__info-container__description`, + `hy-list-item__${this.variant}__info-container__description__${this.headerstyle}`, + ].join(' '); + const classInfoContainerLinkContainer = [`hy-list-item__${this.variant}__info-container__link-container`].join(' '); + const classInfoContainerLinkIcon = [`hy-list-item__${this.variant}__info-container__link__icon`].join(' '); + const classInfoContainerLink = [`hy-list-item__${this.variant}__info-container__link`].join(' '); + const target = this.isExternal ? '_blank' : '_self'; - return ( - <article> - <a class={classAttributes} href={this.url} target={target} aria-label={this.scLabel}> - <div class="hy-list-item__info-container"> - <div class="hy-list-item__info-container__header">{this.itemType}</div> - <div class="hy-list-item__info-container__title">{this.itemTitle}</div> - - <div class="hy-list-item__info-container__link-container"> - <span class="hy-list-item__info-container__link__icon"> - <hy-icon icon={'hy-icon-link'} size={15} /> - </span> - <div class="hy-list-item__info-container__link">{this.url}</div> - </div> - - {this.itemDescription && ( - <div class="hy-list-item__info-container__description">{this.itemDescription}</div> + switch (this.variant) { + case ListItemVariants.degreeProgramme: { + const classImageContainerAttributes = [`hy-list-item__${this.variant}__image-container`].join(' '); + const classImageContainerImage = [`hy-list-item__${this.variant}__image-container__image`].join(' '); + + const classInfoContainerAdditionalInfoAttributes = [ + `hy-list-item__${this.variant}__info-container__additional-info`, + `hy-list-item__${this.variant}__info-container__additional-info__${this.headerstyle}`, + ].join(' '); + + const classInfoContainerOutgoingLink = [ + `hy-list-item__${this.variant}__info-container__outgoing-link`, + `hy-list-item__${this.variant}__info-container__outgoing-link__${this.headerstyle}`, + ].join(' '); + const classInfoContainerOutgoingLinkUrl = [ + `hy-list-item__${this.variant}__info-container__outgoing-link__link`, + ].join(' '); + + const classRelatedInfoContainer = [ + `hy-list-item-wrapper__${this.variant}__related-info`, + `hy-list-item-wrapper__${this.variant}__related-info__${this.headerstyle}`, + ].join(' '); + const classRelatedInfoHeading = [`hy-list-item-wrapper__${this.variant}__related-info__heading`].join(' '); + const classRelatedInfoHeadingLink = [`hy-list-item-wrapper__${this.variant}__related-info__heading__link`].join( + ' ' + ); + const classRelatedInfoHeadingButton = [ + `hy-list-item-wrapper__${this.variant}__related-info__heading__button`, + ].join(' '); + const classRelatedInfoHeadingIcon = [`hy-list-item-wrapper__${this.variant}__related-info__heading__icon`].join( + ' ' + ); + const classRelatedInfoContent = [`hy-list-item-wrapper__${this.variant}__related-info__content`].join(' '); + const classRelatedInfoLink = [`hy-list-item-wrapper__${this.variant}__related-info__link`].join(' '); + + const titleAsId = + this.relatedLinksBlockTitle && this.relatedLinksBlockTitle.length > 0 + ? `${this.itemTitle}-${this.relatedLinksBlockTitle}`.toLowerCase().replace(/\W/g, '-') + : ''; + const accordionItemHref = '#' + titleAsId + '--title'; + + const relatedLinksCount = + this.relatedLinksBlockTitle && this.relatedLinksBlockTitle.length > 0 ? this._relatedLinks.length : ''; + + return [ + <article class={classWrapperAttributes}> + <a class={classAttributes} href={this.url} target={target} aria-label={this.scLabel}> + <div class={classImageContainerAttributes}> + <div class={classImageContainerImage}> + <img + aria-hidden="true" + alt={this.itemImageAlt} + src={this.itemImageUrl} + style={{ + '--minheight': `${this._imageMinHeight}px` as 'minHeight', + '--minwidth': `${this._imageMinWidth}px` as 'minWidth', + }} + /> + </div> + {this.imageLabel && <span>{this.imageLabel}</span>} + </div> + <div class={classInfoContainerAttributes}> + <div class={classInfoContainerDataAttributes}> + <div class={classInfoContainerTitle}>{this.itemTitle}</div> + {this.itemDescription && <div class={classInfoContainerDescription}>{this.itemDescription}</div>} + {this._additionalInfo && ( + <div class={classInfoContainerAdditionalInfoAttributes}> + {this._additionalInfo.map((x) => { + let classInfoContainerAdditionalInfoItem = [ + `hy-list-item__${this.variant}__info-container__additional-info__item`, + `hy-list-item__${this.variant}__info-container__additional-info__item__${this.headerstyle}`, + `${x.isActive.toString() == 'true' ? 'active' : ''}`, + ].join(' '); + return <div class={classInfoContainerAdditionalInfoItem}>{x.text}</div>; + })} + </div> + )} + </div> + <div class={classInfoContainerOutgoingLink}> + <a + href={this.url} + target={target} + aria-label={this.scLabel} + class={classInfoContainerOutgoingLinkUrl} + tabindex="-1" + > + <span class="label">{this.outGoingUrlLabel}</span> + <span class="icon-wrapper"> + <div class="icon"> + <hy-icon icon={'hy-icon-arrow-to-right'} size={32} /> + </div> + </span> + </a> + </div> + </div> + </a> + {this.relatedLinksBlockTitle && ( + <div class={classRelatedInfoContainer}> + <div class={classRelatedInfoHeading}> + <a href={accordionItemHref} class={classRelatedInfoHeadingLink} tabindex="-1"> + <button + aria-expanded="false" + aria-controls={`${titleAsId}--content`} + class={classRelatedInfoHeadingButton} + id={`${titleAsId}--title`} + > + <span class={classRelatedInfoHeadingIcon}> + <hy-icon icon={'hy-icon-caret-down'} size={14} /> + </span> + {this.relatedLinksBlockTitle} ({relatedLinksCount}) + </button> + </a> + </div> + <div + class={classRelatedInfoContent} + aria-labelledBy={`${titleAsId}--title`} + id={`${titleAsId}--content`} + role="region" + aria-hidden="true" + > + {this._relatedLinks.map((x) => { + let relatedLinkTarget = x.isExternal ? '_blank' : '_self'; + return ( + <a class={classRelatedInfoLink} href={x.url} target={target} aria-label={relatedLinkTarget}> + {x.label} + </a> + ); + })} + </div> + </div> )} - </div> - </a> - </article> - ); + </article>, + ]; + } + default: { + return ( + <article> + <a class={classAttributes} href={this.url} target={target} aria-label={this.scLabel}> + <div class={classInfoContainerAttributes}> + <div class={classInfoContainerHeader}>{this.itemType}</div> + <div class={classInfoContainerTitle}>{this.itemTitle}</div> + + <div class={classInfoContainerLinkContainer}> + <span class={classInfoContainerLinkIcon}> + <hy-icon icon={'hy-icon-link'} size={15} /> + </span> + <div class={classInfoContainerLink}>{this.url}</div> + </div> + + {this.itemDescription && <div class={classInfoContainerDescription}>{this.itemDescription}</div>} + </div> + </a> + </article> + ); + } + } } } diff --git a/src/components/hy-list-item/readme.md b/src/components/hy-list-item/readme.md index f9f00fefe6f4b29e3f80198a690658facc788399..09e7cfed73dfe4b6aa141947f88846b1ebab8f1d 100644 --- a/src/components/hy-list-item/readme.md +++ b/src/components/hy-list-item/readme.md @@ -4,14 +4,23 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ----------------- | ------------------ | ----------- | --------- | ----------- | -| `isExternal` | `is-external` | | `boolean` | `false` | -| `itemDescription` | `item-description` | | `string` | `undefined` | -| `itemTitle` | `item-title` | | `string` | `undefined` | -| `itemType` | `item-type` | | `string` | `undefined` | -| `scLabel` | `sc-label` | | `string` | `undefined` | -| `url` | `url` | | `string` | `undefined` | +| Property | Attribute | Description | Type | Default | +| ------------------------ | --------------------------- | ----------- | -------------------------------------------------------------- | -------------------------- | +| `additionalInfo` | `additional-info` | | `AdditionalInfo[] \| string` | `undefined` | +| `headerstyle` | `headerstyle` | | `string` | `'common'` | +| `imageLabel` | `image-label` | | `string` | `undefined` | +| `isExternal` | `is-external` | | `boolean` | `false` | +| `itemDescription` | `item-description` | | `string` | `undefined` | +| `itemImageAlt` | `item-image-alt` | | `string` | `undefined` | +| `itemImageUrl` | `item-image-url` | | `string` | `undefined` | +| `itemTitle` | `item-title` | | `string` | `undefined` | +| `itemType` | `item-type` | | `string` | `undefined` | +| `outGoingUrlLabel` | `out-going-url-label` | | `string` | `'Go to programme page'` | +| `relatedLinks` | `related-links` | | `RelatedLink[] \| string` | `undefined` | +| `relatedLinksBlockTitle` | `related-links-block-title` | | `string` | `undefined` | +| `scLabel` | `sc-label` | | `string` | `undefined` | +| `url` | `url` | | `string` | `undefined` | +| `variant` | `variant` | | `ListItemVariants.default \| ListItemVariants.degreeProgramme` | `ListItemVariants.default` | ## Dependencies diff --git a/src/global/_typography.scss b/src/global/_typography.scss index 5d5c7235aa92eccf15a61264e9f9a96446e0adf1..240bdad251ba64b514fd35ce2872d6a5f880b781 100644 --- a/src/global/_typography.scss +++ b/src/global/_typography.scss @@ -13,6 +13,7 @@ $font-size-xxxx-large: 80px; $base-font-size: $font-size-base; // Font weights +$light: 300; $regular: 400; $semibold: 600; $bold: 700; diff --git a/src/index.html b/src/index.html index 4e94bc6d4c5fd3110873693d29b2cfae927b0841..712a51eeb6041163f155b491cf69315275b91455 100644 --- a/src/index.html +++ b/src/index.html @@ -276,6 +276,104 @@ THIS IS MAIN CONTENT </hy-paragraph-text> + <hy-paragraph-text> + This Is Degree Programmes. Bachelor/Master + </hy-paragraph-text> + <hy-list-item + variant="degree" + item-title="Agricultural sciences – Master’s programme" + item-description="Study Agricultural Sciences and specialise in agrotechnology, animal science, environmental soil science or plant production sciences. University of Helsinki is the only university in Finland to offer academic education in this field." + url="https://www.helsinki.fi/en/faculty-of-arts/admissions/apply-to-the-faculty" + is-external="true" + sc-label="Link opens up in a new tab" + item-image-url="https://www.helsinki.fi/sites/default/files/styles/12_7_small/public/agricultural_sciences_0.jpg?itok=fyfKN1bI" + item-image-alt="degree image" + related-links-block-title="After completion of this bachelor's programme, it is possible to continue in the following master's programmes" + related-links='[ + {"label":"Science – Master’s programme", "url":"https://helsinki.fi", "isExternal":"true","scLabel":"science programme"}, + {"label":"Agricultural, environmental and resource economics – Master’s programme", "url":"https://helsinki.fi", "isExternal":"true","scLabel":"Agricultural programme"}, + {"label":"Atmospheric sciences – Master’s programme", "url":"https://helsinki.fi", "isExternal":"true","scLabel":"Atmospheric programme"} + ]' + image-label="Apply now" + out-going-url-label="Go to programme page" + additional-info='[ + {"text":"Submit application by 31 Jan 2021", "isActive":"true"}]' + > + </hy-list-item> + <hy-list-item + variant="degree" + item-title="Agricultural, environmental and resource economics – Master’s programme" + item-description="Combine economics and natural sciences to become a professional in applied economics in agricultural, environmental and resource-focused fields. You will be well versed in topics such as climate policy, sustainable agriculture and food security." + url="https://www.helsinki.fi/en/faculty-of-arts/admissions/apply-to-the-faculty" + is-external="true" + sc-label="Link opens up in a new tab" + item-image-url="https://www.helsinki.fi/sites/default/files/styles/16_9_huge/public/kukkataedit_ja_-sedaet-6_0.jpg" + item-image-alt="degree image" + out-going-url-label="Go to programme page" + additional-info='[ + {"text":"Next application period starts 1 Dec 2020", "isActive":"false"}]' + > + </hy-list-item> + + <hy-paragraph-text> + This Is Degree Programmes. Doctoral + </hy-paragraph-text> + <hy-list-item + variant="degree" + item-title="Omnipotential sciences – Doctoral programme" + item-description="Study Agricultural Sciences and specialise in agrotechnology, animal science, environmental soil science or plant production sciences. University of Helsinki is the only university in Finland to offer academic education in this field." + url="https://www.helsinki.fi/en/faculty-of-arts/admissions/apply-to-the-faculty" + is-external="true" + sc-label="Link opens up in a new tab" + item-image-url="https://www.helsinki.fi/sites/default/files/styles/12_7_small/public/agricultural_sciences_0.jpg?itok=fyfKN1bI" + item-image-alt="degree image" + image-label="Apply now" + related-links-block-title="After completion of this bachelor's programme, it is possible to continue in the following master's programmes" + related-links='[ + {"label":"Science – Master’s programme", "url":"https://helsinki.fi", "isExternal":"true","scLabel":"science programme"} + ]' + out-going-url-label="Go to programme page" + additional-info='[ + {"text":"Application period for doctoral study rights ends 30 Jan 2021", "isActive":"true"}, + {"text":"Next call for university-funded positions opens 19 Aug 2021", "isActive":"false"}]' + > + </hy-list-item> + <hy-list-item + variant="degree" + item-title="Omnipotential sciences – Doctoral programme" + item-description="Study Agricultural Sciences and specialise in agrotechnology, animal science, environmental soil science or plant production sciences. University of Helsinki is the only university in Finland to offer academic education in this field. Study Agricultural Sciences and specialise in agrotechnology, animal science, environmental soil science or plant production sciences. University of Helsinki is the only university in Finland to offer academic education in this field. Study Agricultural Sciences and specialise in agrotechnology, animal science, environmental soil science or plant production sciences. University of Helsinki is the only university in Finland to offer academic education in this field. Study Agricultural Sciences and specialise in agrotechnology, animal science, environmental soil science or plant production sciences. University of Helsinki is the only university in Finland to offer academic education in this field." + url="https://www.helsinki.fi/en/faculty-of-arts/admissions/apply-to-the-faculty" + is-external="true" + sc-label="Link opens up in a new tab" + item-image-url="https://www.helsinki.fi/sites/default/files/styles/16_9_huge/public/kukkataedit_ja_-sedaet-6_0.jpg" + item-image-alt="degree image" + image-label="Apply now" + out-going-url-label="Go to programme page" + > + </hy-list-item> + <hy-list-item + variant="degree" + item-title="Space age material sciences – Doctoral programme" + item-description="Do you want to shape the future world and understand how the universe is built up? The core scientific disciplines of math, physics, chemistry and computer science set the basis for all modern technology. In the interdisciplinary programme you get to work on the cutting edge of exciting basic research and application development in all of these subjects. +Do you want to shape the future world and understand how the universe is built up? The core scientific disciplines of math, physics, chemistry and computer science set the basis for all modern technology. In the interdisciplinary programme you get to work on the cutting edge of exciting basic research and application development in all of these subjects. +Do you want to shape the future world and understand how the universe is built up? The core scientific disciplines of math, physics, chemistry and computer science set the basis for all modern technology. In the interdisciplinary programme you get to work on the cutting edge of exciting basic research and application development in all of these subjects. +Do you want to shape the future world and understand how the universe is built up? The core scientific disciplines of math, physics, chemistry and computer science set the basis for all modern technology. In the interdisciplinary programme you get to work on the cutting edge of exciting basic research and application development in all of these subjects." + url="https://www.helsinki.fi/en/faculty-of-arts/admissions/apply-to-the-faculty" + is-external="true" + sc-label="Link opens up in a new tab" + item-image-url="https://www.helsinki.fi/sites/default/files/styles/12_7_small/public/agricultural_sciences_0.jpg?itok=fyfKN1bI" + item-image-alt="degree image" + out-going-url-label="Go to programme page" + additional-info='[ + {"text":"Next application period for doctoral study rights starts 15 Jan 2021", "isActive":"false"}, + {"text":"Next call for university-funded positions opens 19 Aug 2021", "isActive":"false"}]' + > + </hy-list-item> + + <hy-paragraph-text> + This Is Search Result Items + </hy-paragraph-text> + <hy-box pt="2" pb="2" align="center"> <hy-checkbox checkbox-id="1" checkbox-value="checkbox_1" checkbox-label="Introduction course"></hy-checkbox> <hy-checkbox checkbox-id="2" checkbox-value="checkbox_2" checkbox-label="Open online course"></hy-checkbox> @@ -353,6 +451,10 @@ > </hy-list-item> + <hy-paragraph-text> + This Is Person card info + </hy-paragraph-text> + <hy-person-card variant="search-panel" category-title="People" diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 5b18b9cc6fa44620e05c833c19d5a04f846b4932..a8715385f72e427e41d21c28799e58f8e3afbee7 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -83,6 +83,11 @@ export enum PersonCardVariants { searchPanel = 'search-panel', } +export enum ListItemVariants { + default = 'default', + degreeProgramme = 'degree', +} + export enum PaginationItemVariants { default = 'default', current = 'current',