diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template_component.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template_component.md
index 2586010a9..c3b3e639e 100644
--- a/.github/PULL_REQUEST_TEMPLATE/pull_request_template_component.md
+++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template_component.md
@@ -16,7 +16,7 @@
* [ ] A11y tests
-* [ ] Ui tests
+* [ ] Ui tests
* [ ] Unit and coverage
* [ ] Test for Aria [matching roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles)
* [ ] [Check the markup](https://validator.w3.org/)
diff --git a/__snapshots__/badge.md b/__snapshots__/badge.md
index dd65d6de5..72543e5ff 100644
--- a/__snapshots__/badge.md
+++ b/__snapshots__/badge.md
@@ -1,10 +1,100 @@
# `badge`
-#### `should internal contents`
+#### `should match internal contents`
```html
-
-
+
+```
+
+#### `should match internal contents (legacy)`
+
+```html
+
+
+
+
+
+```
+
+## `icons`
+
+#### `should have icons when icons are set`
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+#### `should have icons added when icons are set dynamically (property)`
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+#### `should have icons added when icons are set dynamically (attribute)`
+
+```html
+
+
+
+
+
+
+
+
+
+```
+
+#### `should have icons removed when icons are unset (property)`
+
+```html
+
+
+
+
+
+```
+
+#### `should have icon removed when icon is unset (attribute)`
+
+```html
+
+
+
+
```
diff --git a/components/badge/package.json b/components/badge/package.json
index b569a9667..aeb29a60d 100644
--- a/components/badge/package.json
+++ b/components/badge/package.json
@@ -29,8 +29,10 @@
},
"dependencies": {
"@vonage/vvd-core": "2.15.0",
+ "@vonage/vwc-icon": "2.15.0",
"@vonage/vvd-foundation": "2.15.0",
"lit-element": "^2.4.0",
+ "lit-html": "^1.3.0",
"tslib": "^2.3.0"
},
"devDependencies": {
@@ -39,4 +41,4 @@
"@vonage/vvd-umbrella": "2.15.0",
"typescript": "^4.3.2"
}
-}
+}
\ No newline at end of file
diff --git a/components/badge/readme.md b/components/badge/readme.md
index 7217586d9..981c7a77b 100644
--- a/components/badge/readme.md
+++ b/components/badge/readme.md
@@ -1,51 +1,23 @@
-# vwc-select
+# vwc-badge
-This component is an extension of [](https://github.com/material-components/material-components-web-components/tree/master/packages/select)
+Represents a badge custom element.
+badge is a label that holds small amounts of information. A badge can be used to display unread notifications, or to label a block of text. Badges don’t work for navigation because they can't include a hyperlink.
## Properties
-| Property | Modifiers | Type | Description |
-|---------------------------|-----------|--------------------------------------------------|--------------------------------------------------|
-| `disabled` | | `boolean` | |
-| `floatingLabelFoundation` | | `MDCFloatingLabelFoundation \| undefined` | |
-| `helper` | | `string` | |
-| `icon` | | `string` | |
-| `index` | readonly | `number` | |
-| `items` | readonly | `ListItemBase[]` | |
-| `label` | | `string` | |
-| `lineRippleFoundation` | | `MDCLineRippleFoundation \| undefined` | |
-| `naturalMenuWidth` | | `boolean` | |
-| `outlined` | | `boolean` | |
-| `required` | | `boolean` | |
-| `ripple` | readonly | `RippleInterface \| Promise \| undefined` | Implement ripple getter for Ripple integration with mwc-formfield |
-| `selected` | readonly | `ListItemBase \| null` | |
-| `validateOnInitialRender` | | `boolean` | |
-| `validationMessage` | | `string` | |
-| `validity` | readonly | `ValidityState` | |
-| `validityTransform` | | `((value: string, nativeValidity: ValidityState) => Partial) \| null` | |
-| `value` | | `string` | |
+| Property | Attribute | Type | Default |
+| -------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
+| `connotation` | `connotation` | `Connotation.Primary \| Connotation.CTA \| Connotation.Success \| Connotation.Alert \| Connotation.Warning \| Connotation.Info \| undefined` | |
+| `dense` | `dense` | `boolean` | false |
+| `enlarged` | `enlarged` | `boolean` | false |
+| `icon` | `icon` | `string \| undefined` | |
+| `iconTrailing` | `iconTrailing` | `string \| undefined` | |
+| `layout` | `layout` | `Layout.Filled \| Layout.Outlined \| Layout.Soft` | "filled" |
+| `shape` | `shape` | `Shape.Rounded \| Shape.Pill \| undefined` | |
+| `text` | `text` | `string \| undefined` | |
-## Methods
+## Slots
-| Method | Type |
-|---------------------|--------------------------------------------------|
-| `blur` | `(): void` |
-| `checkValidity` | `(): boolean` |
-| `click` | `(): void` |
-| `focus` | `(): void` |
-| `layout` | `(updateItems?: boolean \| undefined): Promise` |
-| `reportValidity` | `(): boolean` |
-| `select` | `(index: number): void` |
-| `setAriaLabel` | `(label: string): void` |
-| `setCustomValidity` | `(message: string): void` |
-
-## Events
-
-| Event | Description |
-|------------|------------------|
-| `action` | {ActionDetail} |
-| `change` | |
-| `closed` | |
-| `invalid` | |
-| `opened` | |
-| `selected` | {SelectedDetail} |
+| Name | Description |
+| --------- | ------------------------------------------------------------------------------------------------------ |
+| _default_ | This is a default/unnamed slot to assign text content. *deprecated* please use _text_ property instead |
diff --git a/components/badge/src/vwc-badge-base.ts b/components/badge/src/vwc-badge-base.ts
index a7aa973f0..4d46e509e 100644
--- a/components/badge/src/vwc-badge-base.ts
+++ b/components/badge/src/vwc-badge-base.ts
@@ -1,6 +1,9 @@
import {
html, LitElement, property, TemplateResult
} from 'lit-element';
+import { nothing } from 'lit-html';
+import { classMap } from 'lit-html/directives/class-map';
+
import { Connotation, Shape, Layout } from '@vonage/vvd-foundation/constants';
import { handleMultipleDenseProps } from '@vonage/vvd-foundation/general-utils';
@@ -21,6 +24,9 @@ type BadgeLayout = Extract<
type BadgeShape = Extract;
+/**
+ * @slot - This is a default/unnamed slot to assign text content. *deprecated* please use _text_ property instead
+ */
export class VWCBadgeBase extends LitElement {
@property({ type: String, reflect: true })
connotation?: BadgeConnotation;
@@ -37,11 +43,38 @@ export class VWCBadgeBase extends LitElement {
@property({ type: Boolean, reflect: true })
enlarged = false;
+ @property({ type: String, reflect: true })
+ text?: string;
+
+ @property({ type: String, reflect: true })
+ icon?: string;
+
+ @property({ type: String, reflect: true })
+ iconTrailing?: string;
+
+ protected renderIcon(type?: string, isTrailingIcon = false): TemplateResult | typeof nothing {
+ const classes = {
+ 'icon--leading': !isTrailingIcon,
+ 'icon--trailing': isTrailingIcon
+ };
+
+ return type ?
+ html``
+ : nothing;
+ }
+
protected updated(changes: Map): void {
- handleMultipleDenseProps(this, changes);
+ handleMultipleDenseProps(this, changes);
}
protected render(): TemplateResult {
- return html``;
+ return html`
+
+ ${this.renderIcon(this.icon)}
+
+ ${this.text || nothing}
+
+ ${this.renderIcon(this.iconTrailing, true)}
+ `;
}
}
diff --git a/components/badge/src/vwc-badge.scss b/components/badge/src/vwc-badge.scss
index e5270fd9a..9389dbd31 100644
--- a/components/badge/src/vwc-badge.scss
+++ b/components/badge/src/vwc-badge.scss
@@ -31,6 +31,17 @@ $vvd-badge-block-size: --#{$namespace}-block-size;
:host {
#{$vvd-badge-block-size}: 24px;
+}
+
+:host([dense]) {
+ #{$vvd-badge-block-size}: 20px;
+}
+
+:host([enlarged]) {
+ #{$vvd-badge-block-size}: 28px;
+}
+
+.vwc-badge {
@include typography.typography-cat-shorthand('caption-bold');
display: inline-block;
padding: 0 10px;
@@ -39,24 +50,24 @@ $vvd-badge-block-size: --#{$namespace}-block-size;
border-radius: var(--#{$namespace}-shape);
color: var(--#{$namespace}-color);
line-height: var(#{$vvd-badge-block-size});
-}
-:host([layout="outlined"i]) {
- --color: var(#{scheme-variables.$vvd-color-on-base});
-}
+ :host([dense]) & {
+ padding: 0 8px;
+ }
-:host([disabled]) {
- background-color: var(#{scheme-variables.$vvd-color-neutral-30});
- color: var(#{scheme-variables.$vvd-color-neutral-50});
+ :host([enlarged]) & {
+ padding: 0 12px;
+ }
}
-:host([dense]) {
- #{$vvd-badge-block-size}: 20px;
- padding: 0 8px;
-}
+.icon {
+ --icon-size: 12px;
+ $margin: 8px;
-:host([enlarged]) {
- #{$vvd-badge-block-size}: 28px;
- padding: 0 12px;
+ &.icon--leading {
+ margin-inline-end: $margin;
+ }
+ &.icon--trailing {
+ margin-inline-start: $margin;
+ }
}
-
diff --git a/components/badge/src/vwc-badge.ts b/components/badge/src/vwc-badge.ts
index cbbc8a5bd..0bd23db5c 100644
--- a/components/badge/src/vwc-badge.ts
+++ b/components/badge/src/vwc-badge.ts
@@ -1,9 +1,14 @@
import '@vonage/vvd-core';
+import '@vonage/vwc-icon';
import { customElement } from 'lit-element';
import { VWCBadgeBase } from './vwc-badge-base.js';
import { style } from './vwc-badge.css.js';
+/**
+ * Represents a badge custom element.
+ * badge is a label that holds small amounts of information. A badge can be used to display unread notifications, or to label a block of text. Badges don’t work for navigation because they can't include a hyperlink.
+ */
@customElement('vwc-badge')
export class VWCBadge extends VWCBadgeBase {
static styles = style;
diff --git a/components/badge/stories/badge-introduction.config.mjs b/components/badge/stories/badge-introduction.config.mjs
new file mode 100644
index 000000000..0673ebdec
--- /dev/null
+++ b/components/badge/stories/badge-introduction.config.mjs
@@ -0,0 +1,14 @@
+export default {
+ sourcePath: '../readme.md',
+ outputName: 'badge-introduction',
+ story: {
+ title: 'Alpha/Components/Badge/Introduction',
+ name: 'Introduction',
+ parameters: {
+ options: {
+ showPanel: false,
+ isToolshown: false
+ }
+ }
+ }
+};
diff --git a/components/badge/stories/badge.stories.js b/components/badge/stories/badge.stories.js
index 4eaefbfa7..4902fa144 100644
--- a/components/badge/stories/badge.stories.js
+++ b/components/badge/stories/badge.stories.js
@@ -7,24 +7,29 @@ export default {
title: 'Components/Badge',
component: 'vwc-badge',
argTypes
-}
+};
-const Template = args => html`I'm a badge`;
+const Template = args => html``;
export const Basic = Template.bind({});
-Basic.args = { connotation: 'cta', layout: 'filled' };
+Basic.args = { connotation: 'cta', layout: 'filled', text: 'badge' };
+
+export const WithIcons = Template.bind({});
+WithIcons.args = {
+ connotation: 'cta', layout: 'filled', text: 'badge', icon: 'check-line', iconTrailing: 'check-line'
+};
export const Soft = Template.bind({});
-Soft.args = { connotation: 'cta', layout: 'soft' };
+Soft.args = { connotation: 'cta', layout: 'soft', text: 'badge' };
export const Outlined = Template.bind({});
-Outlined.args = { layout: 'outlined' };
+Outlined.args = { layout: 'outlined', text: 'badge' };
export const PillShape = Template.bind({});
-PillShape.args = { layout: 'filled', shape: 'pill' };
+PillShape.args = { layout: 'filled', shape: 'pill', text: 'badge' };
export const Dense = Template.bind({});
-Dense.args = { layout: 'filled', dense: '' };
+Dense.args = { layout: 'filled', dense: '', text: 'badge' };
export const Enlarged = Template.bind({});
-Enlarged.args = { layout: 'filled', enlarged: '' };
+Enlarged.args = { layout: 'filled', enlarged: '', text: 'badge' };
diff --git a/components/badge/test/badge.connotation.test.js b/components/badge/test/badge.connotation.test.js
deleted file mode 100644
index fbd37f25a..000000000
--- a/components/badge/test/badge.connotation.test.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import '../vwc-badge.js';
-import {
- waitNextTask,
- textToDomToParent,
- isolatedElementsCreation,
-} from '../../../test/test-helpers.js';
-import {
- assertConnotationAttribute,
- assertConnotationProperty,
-} from '@vonage/vvd-foundation/test/connotation.test.js';
-
-const VWC_BADGE = 'vwc-badge';
-const CONNOTATIONS_SUPPORTED = [
- 'primary',
- 'cta',
- 'success',
- 'alert',
- 'info',
-];
-
-describe('badge connotation', () => {
- const addElement = isolatedElementsCreation();
-
- it(`should sync badge class member 'connotation' and html attribute 'connotation'`, async function () {
- const [badge] = addElement(
- textToDomToParent(`<${VWC_BADGE}>Badge Text${VWC_BADGE}>`)
- );
- await waitNextTask();
-
- const syncMatchFn = connotation => badge.connotation == connotation &&
- badge.getAttribute('connotation') == connotation;
-
- let connotationValue = 'cta';
- badge.connotation = connotationValue;
- await waitNextTask();
- const propertyChangesAffectsAttribute = syncMatchFn(connotationValue);
-
- connotationValue = 'primary';
- badge.setAttribute('connotation', connotationValue);
- await waitNextTask();
- const attributeChangesAffectsProperty = syncMatchFn(connotationValue);
-
- expect(
- propertyChangesAffectsAttribute,
- 'Property change did not apply to attribute'
- ).to.equal(true);
- expect(
- attributeChangesAffectsProperty,
- 'Attribute change did not apply to property'
- ).to.equal(true);
- });
-
- for (const connotation of CONNOTATIONS_SUPPORTED) {
- it(`should reflect '${connotation}' connotation (attribute) visually`, async () => {
- const [badge] = addElement(
- textToDomToParent(`<${VWC_BADGE}>Badge Text${VWC_BADGE}>`)
- );
- await assertConnotationAttribute({
- element: badge,
- connotation: connotation,
- stylesAffected: ['backgroundColor'],
- });
- });
-
- it(`should reflect '${connotation}' connotation (property) visually`, async () => {
- const [badge] = addElement(
- textToDomToParent(`<${VWC_BADGE}>Badge Text${VWC_BADGE}>`)
- );
- await assertConnotationProperty({
- element: badge,
- connotation: connotation,
- stylesAffected: ['backgroundColor'],
- });
- });
- }
-});
diff --git a/components/badge/test/badge.test.js b/components/badge/test/badge.test.js
index e3b790191..161526953 100644
--- a/components/badge/test/badge.test.js
+++ b/components/badge/test/badge.test.js
@@ -1,6 +1,5 @@
import '../vwc-badge.js';
import {
- waitNextTask,
textToDomToParent,
assertComputedStyle,
isolatedElementsCreation,
@@ -22,86 +21,167 @@ describe('badge', () => {
);
});
- it('should internal contents', async () => {
- const [actualElement] = addElement(
- textToDomToParent(`<${VWC_BADGE}>Badge Text${VWC_BADGE}>`)
+ it('should match internal contents', async () => {
+ const [badge] = addElement(
+ textToDomToParent(`<${VWC_BADGE} text="badge">${VWC_BADGE}>`)
);
- await waitNextTask();
- expect(actualElement.shadowRoot.innerHTML).to.equalSnapshot();
+ expect(badge.shadowRoot.innerHTML).to.equalSnapshot();
+ });
+
+ it('should match internal contents (legacy)', async () => {
+ const [badge] = addElement(
+ textToDomToParent(`<${VWC_BADGE}>badge${VWC_BADGE}>`)
+ );
+ await badge.updateComplete;
+ expect(badge.shadowRoot.innerHTML).to.equalSnapshot();
});
describe('typography', function () {
it(`should have set badge (text, default) typography correct`, async function () {
const [badge] = addElement(
- textToDomToParent(`<${VWC_BADGE}>Badge Text${VWC_BADGE}>`)
+ textToDomToParent(`<${VWC_BADGE} text="badge">${VWC_BADGE}>`)
);
- await waitNextTask();
- expect(badge).to.exist;
- assertComputedStyle(badge, { ...(await getTypographyStyle('caption-bold')), lineHeight: '24px' });
+ await badge.updateComplete;
+ const container = badge.shadowRoot.querySelector('.vwc-badge');
+ assertComputedStyle(container, { ...(await getTypographyStyle('caption-bold')), lineHeight: '24px' });
});
});
describe('sizing', () => {
it('should have normal size by default', async () => {
- const addedElements = addElement(
- textToDomToParent(`<${VWC_BADGE}>I'm a badge${VWC_BADGE}>`)
+ const [badge] = addElement(
+ textToDomToParent(`<${VWC_BADGE} text="badge">${VWC_BADGE}>`)
);
- const actualElement = addedElements[0];
- await waitNextTask();
- assertComputedStyle(actualElement, { height: '24px' });
+ await badge.updateComplete;
+ const container = badge.shadowRoot.querySelector('.vwc-badge');
+ assertComputedStyle(container, { height: '24px' });
});
it('should have dense size when dense', async () => {
- const addedElements = addElement(
- textToDomToParent(`<${VWC_BADGE} dense>I'm a badge${VWC_BADGE}>`)
+ const [badge] = addElement(
+ textToDomToParent(`<${VWC_BADGE} dense text="badge">${VWC_BADGE}>`)
);
- const actualElement = addedElements[0];
- await waitNextTask();
- assertComputedStyle(actualElement, { height: '20px' });
+ await badge.updateComplete;
+ const container = badge.shadowRoot.querySelector('.vwc-badge');
+ assertComputedStyle(container, { height: '20px' });
});
it('should have enlarged size when enlarged', async () => {
- const addedElements = addElement(
- textToDomToParent(`<${VWC_BADGE} enlarged>I'm a badge${VWC_BADGE}>`)
+ const [badge] = addElement(
+ textToDomToParent(`<${VWC_BADGE} enlarged text="badge">${VWC_BADGE}>`)
);
- const actualElement = addedElements[0];
- await waitNextTask();
- assertComputedStyle(actualElement, { height: '28px' });
+ await badge.updateComplete;
+ const container = badge.shadowRoot.querySelector('.vwc-badge');
+ assertComputedStyle(container, { height: '28px' });
});
});
describe('shape', () => {
- let actualElement;
+ let badge;
beforeEach(async () => {
- const addedElements = addElement(
+ const [el] = addElement(
textToDomToParent(
- `<${VWC_BADGE} layout="filled">I'm a badge${VWC_BADGE}>`
+ `<${VWC_BADGE} layout="filled" text="badge">${VWC_BADGE}>`
)
);
- await waitNextTask();
- actualElement = addedElements[0];
+ await el.updateComplete;
+ badge = el;
});
it('should have rounded shape by default', async () => {
+ const container = badge.shadowRoot.querySelector('.vwc-badge');
assertComputedStyle(
- actualElement,
+ container,
shapeStyles('rounded', 'badge')
);
});
it('should have rounded shape when shape set to rounded', async () => {
- actualElement.shape = 'rounded';
- await waitNextTask();
+ badge.shape = 'rounded';
+ await badge.updateComplete;
+
+ const container = badge.shadowRoot.querySelector('.vwc-badge');
assertComputedStyle(
- actualElement,
+ container,
shapeStyles('rounded', 'badge')
);
});
it('should have pill shape when shape set to pill', async () => {
- actualElement.shape = 'pill';
- await waitNextTask();
- assertComputedStyle(actualElement, shapeStyles('pill', 'badge'));
+ badge.shape = 'pill';
+ await badge.updateComplete;
+
+ const container = badge.shadowRoot.querySelector('.vwc-badge');
+ assertComputedStyle(container,
+ shapeStyles('pill', 'badge'));
+ });
+ });
+
+ describe('icons', () => {
+ it('should have icons when icons are set', async () => {
+ const [badge] = addElement(
+ textToDomToParent(
+ `<${VWC_BADGE} icon="thumbs-down-line" iconTrailing="thumbs-up-line">${VWC_BADGE}>`
+ )
+ );
+
+ await badge.updateComplete;
+ await badge.updateComplete;
+ expect(badge.shadowRoot.innerHTML).to.equalSnapshot();
+ });
+
+ it('should have icons added when icons are set dynamically (property)', async () => {
+ const [badge] = addElement(
+ textToDomToParent(
+ `<${VWC_BADGE}>${VWC_BADGE}>`
+ )
+ );
+
+ badge.icon = 'thumbs-down-line';
+ badge.iconTrailing = 'thumbs-down-line';
+ await badge.updateComplete;
+ await badge.updateComplete;
+ expect(badge.shadowRoot.innerHTML).to.equalSnapshot();
+ });
+
+ it('should have icons added when icons are set dynamically (attribute)', async () => {
+ const [badge] = addElement(
+ textToDomToParent(
+ `<${VWC_BADGE}>${VWC_BADGE}>`
+ )
+ );
+
+ badge.setAttribute('icon', 'thumbs-down-line');
+ badge.setAttribute('iconTrailing', 'thumbs-up-line');
+ await badge.updateComplete;
+ await badge.updateComplete;
+ expect(badge.shadowRoot.innerHTML).to.equalSnapshot();
+ });
+
+ it('should have icons removed when icons are unset (property)', async () => {
+ const [badge] = addElement(
+ textToDomToParent(
+ `<${VWC_BADGE} icon="thumbs-down-line" iconTrailing="thumbs-up-line">${VWC_BADGE}>`
+ )
+ );
+
+ badge.icon = undefined;
+ badge.iconTrailing = undefined;
+ await badge.updateComplete;
+ expect(badge.shadowRoot.innerHTML).to.equalSnapshot();
+ });
+
+ it('should have icon removed when icon is unset (attribute)', async () => {
+ const [badge] = addElement(
+ textToDomToParent(
+ `<${VWC_BADGE} icon="thumbs-down-line" iconTrailing="thumbs-up-line">${VWC_BADGE}>`
+ )
+ );
+
+ badge.removeAttribute('icon');
+ badge.removeAttribute('iconTrailing');
+ await badge.updateComplete;
+ expect(badge.shadowRoot.innerHTML).to.equalSnapshot();
});
});
});
diff --git a/ui-tests/snapshots/vwc-badge.png b/ui-tests/snapshots/vwc-badge.png
index 184d5972c..ddcbe238a 100644
Binary files a/ui-tests/snapshots/vwc-badge.png and b/ui-tests/snapshots/vwc-badge.png differ
diff --git a/ui-tests/tests/vwc-badge/index.js b/ui-tests/tests/vwc-badge/index.js
index cbc9dc623..9bb9c7d9d 100644
--- a/ui-tests/tests/vwc-badge/index.js
+++ b/ui-tests/tests/vwc-badge/index.js
@@ -4,15 +4,20 @@ import '@vonage/vwc-badge';
export async function createElementVariations(wrapper) {
const badgeElementWrapper = document.createElement('div');
- badgeElementWrapper.innerHTML =
- `
-I'm a badge
-I'm a badge
-I'm a badge
-I'm a badge
-I'm a badge
-I'm a badge
-`;
+ badgeElementWrapper.innerHTML = `
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
wrapper.appendChild(badgeElementWrapper);
await vvdCore.settled;