coral-spectrum/coral-component-status/src/scripts/Status.js
/**
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import {BaseComponent} from '../../../coral-base-component';
import {transform, validate} from '../../../coral-utils';
import {Decorator} from '../../../coral-decorator';
/**
Enumeration for {@link Status} variants.
@typedef {Object} StatusVariantEnum
@property {String} NEUTRAL
A default semantic neutral status.
@property {String} WARNING
A notice semantic status.
@property {String} SUCCESS
A positive semantic status.
@property {String} ERROR
A negative semantic status.
@property {String} INFO
An informative semantic status.
*/
const variant = {
NEUTRAL: 'neutral',
ERROR: 'error',
WARNING: 'warning',
SUCCESS: 'success',
INFO: 'info'
};
/**
Enumeration for {@link Status} colors.
@typedef {Object} StatusColorEnum
@property {String} DEFAULT
@property {String} CELERY
@property {String} YELLOW
@property {String} FUCHSIA
@property {String} INDIGO
@property {String} SEA_FOAM
@property {String} CHARTREUSE
@property {String} MAGENTA
@property {String} PURPLE
*/
const color = {
DEFAULT: '',
CELERY: 'celery',
YELLOW: 'yellow',
FUCHSIA: 'fuchsia',
INDIGO: 'indigo',
SEA_FOAM: 'seafoam',
CHARTREUSE: 'chartreuse',
MAGENTA: 'magenta',
PURPLE: 'purple'
};
const CLASSNAME = '_coral-StatusLight';
const variantMapping = {
SUCCESS: 'positive',
ERROR: 'negative',
WARNING: 'notice'
};
const ALL_VARIANT_CLASSES = [];
for (const variantValue in variant) {
ALL_VARIANT_CLASSES.push(`${CLASSNAME}--${variantMapping[variantValue] || variant[variantValue]}`);
}
const ALL_COLOR_CLASSES = [];
for (const colorValue in color) {
ALL_COLOR_CLASSES.push(`${CLASSNAME}--${color[colorValue]}`);
}
/**
@class Coral.Status
@classdesc A Status component to describe the condition of another entity. They can be used to convey semantic meaning
such as statuses and categories.
@htmltag coral-status
@extends {HTMLElement}
@extends {BaseComponent}
*/
const Status = Decorator(class extends BaseComponent(HTMLElement) {
/** @ignore */
constructor() {
super();
// Prepare templates
this._elements = {
// Fetch or create the content zone element
label: this.querySelector('coral-status-label') || document.createElement('coral-status-label')
};
}
/**
Whether the status is disabled or not.
@type {Boolean}
@default false
@htmlattribute disabled
@htmlattributereflected
*/
get disabled() {
return this._disabled || false;
}
set disabled(value) {
this._disabled = transform.booleanAttr(value);
this._reflectAttribute('disabled', this._disabled);
this[this._disabled ? 'setAttribute' : 'removeAttribute']('aria-disabled', this._disabled);
}
/**
The status variant. See {@link StatusVariantEnum}.
When a status has a semantic meaning, it should use semantic colors.
@type {String}
@default StatusVariantEnum.NEUTRAL
@htmlattribute variant
@htmlattributereflected
*/
get variant() {
return this._variant || variant.NEUTRAL;
}
set variant(value) {
value = transform.string(value).toLowerCase();
this._variant = validate.enumeration(variant)(value) && value || variant.NEUTRAL;
this._reflectAttribute('variant', this._variant);
this.classList.remove(...ALL_VARIANT_CLASSES);
this.classList.add(`${CLASSNAME}--${variantMapping[this._variant.toUpperCase()] || this._variant}`);
}
/**
The status color. See {@link StatusColorEnum}.
When a status is used to color code categories and labels commonly found in data visualization, they should use
colors.
The ideal usage for colors is when there are 8 or fewer categories or labels being color coded.
Use them in the following order to ensure the greatest possible color differences for multiple forms of color
blindness:
- Indigo
- Celery
- Magenta
- Yellow
- Fuchsia
- Seafoam
- Chartreuse
- Purple
If a color is set, it'll override any semantic variant.
@type {String}
@default StatusColorEnum.DEFAULT
@htmlattribute color
@htmlattributereflected
*/
get color() {
return this._color || color.DEFAULT;
}
set color(value) {
value = transform.string(value).toLowerCase();
this._color = validate.enumeration(color)(value) && value || color.DEFAULT;
this._reflectAttribute('color', this._color);
this.classList.remove(...ALL_COLOR_CLASSES);
if (this._color !== color.DEFAULT) {
this.classList.add(`${CLASSNAME}--${this._color}`);
}
}
/**
The status label element.
@type {StatusLabel}
@contentzone
*/
get label() {
return this._getContentZone(this._elements.label);
}
set label(value) {
this._setContentZone('label', value, {
handle: 'label',
tagName: 'coral-status-label',
insert: function (label) {
this.appendChild(label);
}
});
}
get _contentZones() {
return {
'coral-status-label': 'label'
};
}
/**
Returns {@link Status} variants.
@return {StatusVariantEnum}
*/
static get variant() {
return variant;
}
/**
Returns {@link Status} colors.
@return {StatusColorEnum}
*/
static get color() {
return color;
}
/** @ignore */
static get observedAttributes() {
return super.observedAttributes.concat(['variant', 'color', 'disabled']);
}
/** @ignore */
render() {
super.render();
this.classList.add(CLASSNAME);
// Default reflected attributes
if (!this._variant) {
this.variant = variant.NEUTRAL;
}
if (!this._color) {
this.color = color.DEFAULT;
}
// Fetch or create the content content zone element
const label = this._elements.label;
// This stops the content zone from being voracious
if (!label.parentNode) {
// move the contents into the content zone
while (this.firstChild) {
label.appendChild(this.firstChild);
}
}
// Assign the content zone moving it into place
this.label = label;
}
});
export default Status;