Skip to content

Commit

Permalink
refactor(ui5-card): header slot is added (#3490)
Browse files Browse the repository at this point in the history
Extract the Card's header part as a separate component with all the related API, called CardHeader and introduce new slot "header" in the Card.

BREAKING CHANGE: titleText, subtitleText, status, headerInteractive properties, action and avatar slots, and header-click events are not available anymore. Instead, use the newly created CardHeader component, where all these properties are available ("headerInteractive" becomes "interactive" and "header-click" becomes "click"). In addition, the Card now has a "header" slot, for which you can use the CardHeader.
  • Loading branch information
GerganaKremenska committed Jul 28, 2021
1 parent e335f52 commit 86f0333
Show file tree
Hide file tree
Showing 16 changed files with 1,209 additions and 929 deletions.
7 changes: 4 additions & 3 deletions docs/Public Module Imports.md
Expand Up @@ -25,9 +25,10 @@ For API documentation and samples, please check the [UI5 Web Components Playgrou
| Avatar | `ui5-avatar` | `import "@ui5/webcomponents/dist/Avatar.js";` |
| Avatar Group | `ui5-avatar-group` | `import "@ui5/webcomponents/dist/AvatarGroup.js";` |
| Badge | `ui5-badge` | `import "@ui5/webcomponents/dist/Badge.js";` |
| Busy Indicator | `ui5-busy-indicator` | `import "@ui5/webcomponents/dist/BusyIndicator.js";` |
| Busy Indicator | `ui5-busy-indicator` | `import "@ui5/webcomponents/dist/BusyIndicator.js";` |
| Button | `ui5-button` | `import "@ui5/webcomponents/dist/Button.js";` |
| Card | `ui5-card` | `import "@ui5/webcomponents/dist/Card.js";` |
| CardHeader | `ui5-card-header` | `import "@ui5/webcomponents/dist/CardHeader.js";` |
| Carousel | `ui5-carousel` | `import "@ui5/webcomponents/dist/Carousel.js";` |
| Checkbox | `ui5-checkbox` | `import "@ui5/webcomponents/dist/CheckBox.js";` |
| Color Palette | `ui5-color-palette` | `import "@ui5/webcomponents/dist/ColorPalette.js";` |
Expand Down Expand Up @@ -57,8 +58,8 @@ For API documentation and samples, please check the [UI5 Web Components Playgrou
| Responsive Popover | `ui5-responsive-popover` | `import "@ui5/webcomponents/dist/ResponsivePopover.js";` |
| Select | `ui5-select` | `import "@ui5/webcomponents/dist/Select.js";` |
| Select Option | `ui5-option` | comes with `ui5-select ` |
| Segmented Button | `ui5-segmented-button` | `import "@ui5/webcomponents/dist/SegmentedButton.js";` |
| Segmented Button Item | `ui5-segmented-button-item`| comes with `ui5-segmented-button ` |
| Segmented Button | `ui5-segmented-button` | `import "@ui5/webcomponents/dist/SegmentedButton.js";` |
| Segmented Button Item | `ui5-segmented-button-item`| comes with `ui5-segmented-button ` |
| Suggestion Item | `ui5-suggestion-item` | comes with `InputSuggestions.js` feature - see below |
| Slider | `ui5-slider` | `import "@ui5/webcomponents/dist/Slider.js";` |
| Step Input | `ui5-step-input` | `import "@ui5/webcomponents/dist/StepInput.js";` |
Expand Down
6 changes: 3 additions & 3 deletions packages/fiori/test/pages/Timeline.html
Expand Up @@ -74,9 +74,9 @@ <h1 class="header-title">ui5-timeline</h1>
<div class="samples">
<h2>Timeline within Card</h2>
<div class="sample">
<ui5-card
heading="Upcoming Activities"
subtitle="For Today">
<ui5-card>
<ui5-card-header slot="header" title-text="Upcoming Activities" subtitle-text="For Today">
</ui5-card-header>
<ui5-timeline>
<ui5-timeline-item id="test-item" title-text="called" subtitle-text="20.02.2017 11:30" icon="phone" name="Stanislava Baltova" name-clickable></ui5-timeline-item>
<ui5-timeline-item id="test-item" title-text="called" subtitle-text="20.02.2017 11:30" icon="phone" name="Stanislava Baltova"></ui5-timeline-item>
Expand Down
1 change: 1 addition & 0 deletions packages/main/bundle.common.js
Expand Up @@ -36,6 +36,7 @@ import Badge from "./dist/Badge.js";
import BusyIndicator from "./dist/BusyIndicator.js";
import Button from "./dist/Button.js";
import Card from "./dist/Card.js";
import CardHeader from "./dist/CardHeader.js";
import Carousel from "./dist/Carousel.js";
import CheckBox from "./dist/CheckBox.js";
import ColorPalette from "./dist/ColorPalette.js";
Expand Down
40 changes: 6 additions & 34 deletions packages/main/src/Card.hbs
@@ -1,44 +1,16 @@
<div
class="{{classes.main}}"
class="{{classes}}"
dir="{{effectiveDir}}"
role="region"
aria-label="{{ariaLabelText}}"
aria-labelledby="{{ariaLabelledByCard}}">
aria-labelledby="{{ariaLabelledByCard}}"
>
<!-- header -->
{{#if hasHeader}}
<div class="{{classes.header}}"
@click="{{_headerClick}}"
@keydown="{{_headerKeydown}}"
@keyup="{{_headerKeyup}}"
role="{{ariaHeaderRole}}"
aria-labelledby="{{ariaLabelledByHeader}}"
aria-level="{{ariaLevel}}"
aria-roledescription="{{ariaCardHeaderRoleDescription}}"
tabindex="0">

{{#if hasAvatar}}
<div id="{{_id}}-avatar" class="ui5-card-avatar" aria-label="{{ariaCardAvatarLabel}}">
<slot name="avatar"></slot>
</div>
{{/if}}

<div class="ui5-card-header-text">
{{#if titleText}}
<div id="{{_id}}-title" class="ui5-card-title" part="title">{{titleText}}</div>
{{/if}}

{{#if subtitleText}}
<div id="{{_id}}-subtitle" class="ui5-card-subtitle" part="subtitle">{{subtitleText}}</div>
{{/if}}
</div>

{{#if hasAction}}
<slot name="action"></slot>
{{else}}
<span id="{{_id}}-status" part="status" class="ui5-card-status">{{status}}</span>
{{/if}}
<div class="ui5-card-header-root">
<slot name="header"></slot>
</div>
{{/if}}

<div role="group" aria-label="{{ariaCardContentLabel}}">
<slot></slot>
</div>
Expand Down
221 changes: 20 additions & 201 deletions packages/main/src/Card.js
Expand Up @@ -2,16 +2,12 @@ import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
import litRender from "@ui5/webcomponents-base/dist/renderer/LitRenderer.js";
import { fetchI18nBundle, getI18nBundle } from "@ui5/webcomponents-base/dist/i18nBundle.js";
import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AriaLabelHelper.js";
import { isSpace, isEnter } from "@ui5/webcomponents-base/dist/Keys.js";
import CardTemplate from "./generated/templates/CardTemplate.lit.js";
import Icon from "./Icon.js";

import {
ARIA_ROLEDESCRIPTION_CARD,
AVATAR_TOOLTIP,
ARIA_LABEL_CARD_CONTENT,
ARIA_ROLEDESCRIPTION_CARD_HEADER,
ARIA_ROLEDESCRIPTION_INTERACTIVE_CARD_HEADER,
} from "./generated/i18n/i18n-defaults.js";

// Styles
Expand All @@ -38,82 +34,20 @@ const metadata = {
},

/**
* Defines the visual representation in the header of the card.
* Supports images and icons.
* Defines the header of the component.
* <br><br>
* <b>Note:</b>
* SAP-icons font provides numerous options. To find all the available icons, see the
* <ui5-link target="_blank" href="https://openui5.hana.ondemand.com/test-resources/sap/m/demokit/iconExplorer/webapp/index.html" class="api-table-content-cell-link">Icon Explorer</ui5-link>.
* <b>Note:</b> Use <code>ui5-card-header</code> for the intended design.
* @type {HTMLElement[]}
* @slot
* @public
*/
avatar: {
type: HTMLElement,
},

/**
* Defines an action, displayed in the right most part of the header.
* <br><br>
* <b>Note:</b> If set, the <code>status</code> text will not be displayed,
* you can either have <code>action</code>, or <code>status</code>.
* @type {HTMLElement[]}
* @slot
* @since 1.0.0-rc.15
* @slot content
* @public
* @since 1.0.0-rc.8
*/
action: {
header: {
type: HTMLElement,
},
},
properties: /** @lends sap.ui.webcomponents.main.Card.prototype */ {

/**
* Defines the title displayed in the component header.
* @type {string}
* @defaultvalue ""
* @public
* @since 1.0.0-rc.15
*/
titleText: {
type: String,
},

/**
* Defines the subtitle displayed in the component header.
* @type {string}
* @defaultvalue ""
* @public
* @since 1.0.0-rc5
*/
subtitleText: {
type: String,
},

/**
* Defines the status displayed in the component header.
* <br><br>
* <b>Note:</b> If the <code>action</code> slot is set, the <code>status</code> will not be displayed,
* you can either have <code>action</code>, or <code>status</code>.
* @type {string}
* @defaultvalue ""
* @public
*/
status: {
type: String,
},

/**
* Defines if the component header would be interactive,
* e.g gets hover effect, gets focused and <code>headerPress</code> event is fired, when it is pressed.
* @type {boolean}
* @defaultvalue false
* @public
*/
headerInteractive: {
type: Boolean,
},

/**
* Sets the accessible aria name of the component.
*
Expand All @@ -138,25 +72,8 @@ const metadata = {
type: String,
defaultValue: "",
},

_headerActive: {
type: Boolean,
noAttribute: true,
},
},
events: /** @lends sap.ui.webcomponents.main.Card.prototype */ {

/**
* Fired when the component header is activated
* by mouse/tap or by using the Enter or Space key.
* <br><br>
* <b>Note:</b> The event would be fired only if the <code>headerInteractive</code> property is set to true.
* @event sap.ui.webcomponents.main.Card#header-click
* @public
* @since 0.10.0
*/
"header-click": {},
},
events: /** @lends sap.ui.webcomponents.main.Card.prototype */ {},
};

/**
Expand All @@ -166,26 +83,19 @@ const metadata = {
* The <code>ui5-card</code> is a component that represents information in the form of a
* tile with separate header and content areas.
* The content area of a <code>ui5-card</code> can be arbitrary HTML content.
* The header can be used through several properties, such as: <code>titleText</code>, <code>subtitleText</code>, <code>status</code>
* and two slots: <code>avatar</code> and <code>action</code>.
* The header can be used through slot <code>header</code>. For which there is a <code>ui5-card-header</code> component to achieve the card look and fill.
*
* <h3>Keyboard handling</h3>
* In case you enable <code>headerInteractive</code> property, you can press the <code>ui5-card</code> header by Space and Enter keys.
* Note: We recommend the usage of <code>ui5-card-header</code> for the header slot, so advantage can be taken for keyboard handling, styling and accessibility.
*
* <h3>CSS Shadow Parts</h3>
*
* <ui5-link target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/::part">CSS Shadow Parts</ui5-link> allow developers to style elements inside the Shadow DOM.
* <br>
* The <code>ui5-card</code> exposes the following CSS Shadow Parts:
* <ul>
* <li>title - Used to style the title of the card</li>
* <li>subtitle - Used to style the subtitle of the card</li>
* <li>status - Used to style the status of the card</li>
* </ul>
*
* <h3>ES6 Module Import</h3>
*
* <code>import "@ui5/webcomponents/dist/Card";</code>
* <br>
* <code>import "@ui5/webcomponents/dist/CardHeader.js";</code> (for <code>ui5-card-header</code>)
*
* @constructor
* @author SAP SE
Expand Down Expand Up @@ -219,36 +129,13 @@ class Card extends UI5Element {

get classes() {
return {
main: {
"ui5-card-root": true,
"ui5-card--nocontent": !this.content.length,
},
header: {
"ui5-card-header": true,
"ui5-card-header--interactive": this.headerInteractive,
"ui5-card-header--active": this.headerInteractive && this._headerActive,
},
"ui5-card-root": true,
"ui5-card--nocontent": !this.content.length,
};
}

get icon() {
return !!this.avatar && this.avatar.startsWith("sap-icon://");
}

get image() {
return !!this.avatar && !this.icon;
}

get ariaHeaderRole() {
return this.headerInteractive ? "button" : "heading";
}

get ariaLevel() {
return this.headerInteractive ? undefined : "3";
}

get hasHeader() {
return !!(this.titleText || this.subtitleText || this.status || this.hasAction || this.avatar);
return !!this.header.length;
}

get ariaLabelText() {
Expand All @@ -259,46 +146,18 @@ class Card extends UI5Element {
return this.i18nBundle.getText(ARIA_ROLEDESCRIPTION_CARD);
}

get ariaCardHeaderRoleDescription() {
return this.headerInteractive ? this.i18nBundle.getText(ARIA_ROLEDESCRIPTION_INTERACTIVE_CARD_HEADER) : this.i18nBundle.getText(ARIA_ROLEDESCRIPTION_CARD_HEADER);
}

get ariaCardAvatarLabel() {
return this.i18nBundle.getText(AVATAR_TOOLTIP);
}

get ariaCardContentLabel() {
return this.i18nBundle.getText(ARIA_LABEL_CARD_CONTENT);
}

get ariaLabelledByHeader() {
const labels = [];

if (this.subtitleText) {
labels.push(`${this._id}-subtitle`);
}

if (this.status) {
labels.push(`${this._id}-status`);
}

if (this.hasAvatar) {
labels.push(`${this._id}-avatar`);
}

return labels.length !== 0 ? labels.join(" ") : undefined;
}

get ariaLabelledByCard() {
return this.titleText ? `${this._id}-title ${this._id}-desc` : `${this._id}-desc`;
}

get hasAvatar() {
return !!this.avatar.length;
}

get hasAction() {
return !!this.action.length;
let labels;
if (this.hasHeader) {
labels = this.header[0].hasAttribute("title-text") ? `${this._id}--header-title ${this._id}-desc` : `${this._id}-desc`;
} else {
labels = `${this._id}-desc`;
}
return labels;
}

static get dependencies() {
Expand All @@ -308,46 +167,6 @@ class Card extends UI5Element {
static async onDefine() {
await fetchI18nBundle("@ui5/webcomponents");
}

_headerClick() {
if (this.headerInteractive) {
this.fireEvent("header-click");
}
}

_headerKeydown(event) {
if (!this.headerInteractive) {
return;
}

const enter = isEnter(event);
const space = isSpace(event);

this._headerActive = enter || space;

if (enter) {
this.fireEvent("header-click");
return;
}

if (space) {
event.preventDefault();
}
}

_headerKeyup(event) {
if (!this.headerInteractive) {
return;
}

const space = isSpace(event);

this._headerActive = false;

if (space) {
this.fireEvent("header-click");
}
}
}

Card.define();
Expand Down

0 comments on commit 86f0333

Please sign in to comment.