Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/chat storybook updates #95

Merged
26 changes: 26 additions & 0 deletions packages/chat/components/card/__stories__/card.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @license
*
* Copyright IBM Corp. 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import '../card';
import { html } from 'lit';

// More on how to set up stories at: https://storybook.js.org/docs/web-components/writing-stories/introduction
export default {
title: 'Components/Chat/Card',
tags: ['autodocs'],
};

export const Default = {
/**
* Renders the template for Storybook
*
* @returns {TemplateResult<1>}
*/
render: () => html` <c4ai-card> </c4ai-card>`,
};
32 changes: 32 additions & 0 deletions packages/chat/components/card/card.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @license
*
* Copyright IBM Corp. 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { customElement } from 'lit/decorators.js';
import { settings } from '@carbon/ai-utilities/es/settings/index.js';
import card from './src/card.js';
import { cardTemplate } from './src/card.template.js';

const { stablePrefix: c4aiPrefix } = settings;

/**
* Constructed class functionality for the test input custom element
*/
@customElement(`${c4aiPrefix}--chat-card`)
class C4AICard extends card {
/**
* Renders the template while passing in class functionality
*
* @returns {TemplateResult<1>}
*/
render() {
return cardTemplate(this);
}
}

export default C4AICard;
103 changes: 103 additions & 0 deletions packages/chat/components/card/src/card.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* Copyright IBM Corp. 2023, 2024
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

@use '../../../../../globals/scss/vars' as *;

@use '@carbon/styles/scss/theme' as *;

:host(#{$c4ai-prefix}--chat-card) {
display: flex;
overflow: hidden;
flex-direction: column;
padding: 0;
border: 1px solid rgba(255, 255, 255, 1e-5);
border-radius: 8px;

background: #393939;
inline-size: 240px;

.#{$c4ai-prefix}--chat-card-image-container {
overflow: hidden;
flex: 1;
block-size: 120px;
max-block-size: 120px;
}

.#{$c4ai-prefix}--chat-card-image-container img {
overflow: hidden;
background: #ccebff;
block-size: auto;
inline-size: 100%;
min-block-size: 120px;
}

.#{$c4ai-prefix}--chat-card-image-container video {
overflow: hidden;
background: #000000;
block-size: auto;
inline-size: 100%;
max-block-size: 120px;
}

.#{$c4ai-prefix}--chat-card-detail-container {
display: flex;
overflow: hidden;
flex: 1;
flex-direction: column;
justify-content: space-between;
padding: 20px 15px 10px;
block-size: 90px;
}

.#{$c4ai-prefix}--chat-card-detail-title {
overflow: hidden;
align-self: flex-start;
color: #f4f4f4;
font-size: 16px;
inline-size: 100%;
white-space: nowrap;
}

.#{$c4ai-prefix}--chat-card-detail-description {
overflow: hidden;
flex-grow: 1;
font-size: 13px;
padding-block-start: 10px;
}

.#{$c4ai-prefix}--chat-card-loader {
flex-grow: 2;
background-color: #78a9ff;
block-size: 240px;
}

.#{$c4ai-prefix}--chat-card-detail-link-container {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
}

.#{$c4ai-prefix}--chat-card-detail-link {
overflow: hidden;
color: #0f62fe;
text-decoration: none;
white-space: nowrap;

a {
color: #0f62fe;
text-decoration: none;
}
}
.#{$c4ai-prefix}--chat-card-detail-link-icon {
padding: 3px;
color: #0f62fe;
}
.#{$c4ai-prefix}--chat-card-detail-link-icon svg {
fill: #0f62fe;
}
}
69 changes: 69 additions & 0 deletions packages/chat/components/card/src/card.template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @license
*
* Copyright IBM Corp. 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { html } from 'lit';
import { settings } from '@carbon/ai-utilities/es/settings/index.js';
const { stablePrefix: c4aiPrefix } = settings;
import ArrowRight16 from '@carbon/web-components/es/icons/arrow--right/16';

/**
* Lit template for card
*
* @param {object} customElementClass Class functionality for the custom element
* @returns {TemplateResult<1>} Lit html template
*/
export function cardTemplate(customElementClass) {
const {
_linkPreviewData: linkPreview,
type: type,
content: content,
} = customElementClass;
return html`<div class="${c4aiPrefix}--chat-card">
${linkPreview !== null
? html` ${type === 'url' && linkPreview.image_url !== null
? html` <div class="${c4aiPrefix}--chat-card-image-container">
<img
class="${c4aiPrefix}--chat-card-image"
src="${linkPreview.image_url}" />
</div>`
: type === 'video'
? html` <div class="${c4aiPrefix}--chat-card-image-container">
<video controls>
<source src="${content}" type="video/webm" />
</video>
</div>`
: html``}

<div class="${c4aiPrefix}--chat-card-detail-container">
<div class="${c4aiPrefix}--chat-card-detail-title">
${linkPreview.title}
</div>
<div class="${c4aiPrefix}--chat-card-detail-description">
${linkPreview.description}
</div>
<div class="${c4aiPrefix}--chat-card-detail-link-container">
<a
class="${c4aiPrefix}--chat-card-detail-link"
href="${linkPreview.link}"
target="_blank"
>${linkPreview.shortenedUrl}</a
>

<div class="${c4aiPrefix}--chat-card-detail-link-icon">
<a href="${linkPreview.link}" target="_blank"
>${ArrowRight16()}</a
>
</div>
</div>
</div>`
: html`<div class="${c4aiPrefix}--chat-card-loader">
Loading Media Card...
</div>`}
</div>`;
}
139 changes: 139 additions & 0 deletions packages/chat/components/card/src/card.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* @license
*
* Copyright IBM Corp. 2023
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { LitElement } from 'lit';
import { property } from 'lit/decorators.js';

// @ts-ignore
import styles from './card.scss?inline';
/**
* Input component using search typeahead api
*/
export default class card extends LitElement {
static styles = styles;
/**
* Array of subelements parsed from API reply
*/
@property({ type: Object, attribute: 'card-elements', reflect: true })
cardElements;

/**
* url content from parent
*/
@property({ type: String, attribute: 'content', reflect: true })
content;

/**
* card type to differentiate between url and videos
*/
@property({ type: String, attribute: 'type', reflect: true })
type;

/**
* link preview object to be invoked when url object is rendered
*/
private _linkPreviewData: any = null;

/** detect when component is rendered to process rawtext
*/
firstUpdated() {
if (this.cardElements == null) {
this._getSitePreviewData(this.content);
} else {
this._linkPreviewData = this.cardElements;
this.requestUpdate();
}

/*if (changedProperties.has('_messageElements')) {
const messageUpdateEvent = new CustomEvent('message-updated', {
detail: { index: this.index, messageElements: this._messageElements },
bubbles: true,
composed: true,
});
this.dispatchEvent(messageUpdateEvent);
}*/
}

/** _formatURL - helper function to display a URL's name without
* @param {string} url - url text that needs to be trimmed for display in the card object
*/
_getShortenedURL(url) {
try {
const host = new URL(url).host;
return host;
} catch (error) {
return 'Site url';
}
}

/** _formatURL - helper function to display a URL's name without
* @param {string} url - url text that needs to be trimmed for display in the card object
*/
_getSiteName(url) {
try {
const domain = new URL(url).hostname.replace(/^www\./, '').split('.')[0];
const formattedName = domain.charAt(0).toUpperCase() + domain.slice(1);
return formattedName;
} catch (error) {
return 'Site name';
}
}

/** get url preview with title, desciption and og:image to preview site card object
* @param {string} url - desired URL for preview
*/
async _getSitePreviewData(url) {
try {
const preview = await this._previewData(url);
console.log(preview);
if (!preview.title) {
preview.title = this._getSiteName(url);
}
preview.shortenedUrl = this._getShortenedURL(url);
this._linkPreviewData = preview;
this.requestUpdate();
} catch (error) {
const backUpName = this._getSiteName(url);
this._linkPreviewData = {
title: backUpName,
'img-url': null,
description: null,
link: url,
};
console.log(this._linkPreviewData);
this.requestUpdate();
}
}

/** conditional url fetchn function
* @param {string} url - url to fetch
*/
async _previewData(url) {
const API_URL = 'http://localhost:5001/get_preview';

const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: url }),
};

try {
return await fetch(API_URL, requestOptions)
.then((response) => response.json())
.then((response) => {
return response;
});
} catch (error: any) {
return {
reply: 'Error reaching: ' + API_URL + ' Details: ' + error.message,
failed: true,
};
}
}
}
Loading
Loading