Skip to content

Commit

Permalink
feat(SFINT-3167): Add ActionButton Component (#74)
Browse files Browse the repository at this point in the history
* feat(SFINT-3167): Add ActionButton component.

* feat(SFINT-3167): Improve ActionButton hover style.

* chore(SFINT-3167): Fix linting

* chore(SFINT-3167): Improve styling rules

* feat(SFINT-3167): Add component documentation

* feat(SFINT-3167): Improve unit tests
Add warning when click handler is not set.
Validate expectations using DOM elements instead of internal method calls.

* feat(SFINT-3167): Improve init of click option

* feat(SFINT-3167): Mark click option as required
  • Loading branch information
lbergeron authored Jun 12, 2020
1 parent c455960 commit 03a1a47
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 0 deletions.
65 changes: 65 additions & 0 deletions pages/action_button.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, height=device-height" />
<title>Development - Action Button</title>
<link rel="stylesheet" href="../css/CoveoFullSearch.css" />
<link rel="stylesheet" href="../css/CoveoJsSearchExtensions.css" />
<script src="../js/CoveoJsSearch.Lazy.js"></script>
<script src="../commonjs/CoveoJsSearchExtensions.js"></script>
<script>
let attachedIds = [];
document.addEventListener('DOMContentLoaded', function() {
Coveo.SearchEndpoint.configureSampleEndpointV2();
Coveo.init(document.body, {});
});
</script>
</head>

<body id="search" class="CoveoSearchInterface" data-enable-history="false" style="padding: 1em;">
<span class="CoveoAnalytics"></span>
<div class="coveo-tab-section">
<a class="CoveoTab" data-id="All" data-caption="All Content"></a>
</div>
<div class="coveo-search-section">
<div class="CoveoSearchbox" data-enable-omnibox="true"></div>
</div>

<div>
<h2>Button with icon only</h2>
<br />
<button
class="CoveoActionButton"
data-icon='&lt;svg width=".5em" height=".5em" viewBox="0 0 20 20"&gt;&lt;path d="M4 5h7v1H4V5m0 3h7v1H4V8m0 3h7v1H4v-1"&gt;&lt;/path&gt;&lt;path d="M15 1c.009-.525.066-1-1-1H1.002c-.651 0-1 .33-1 1v15c0 .66.351 1 1 1H3v2c.075.546.383 1 1 1h13c.718 0 1-.295 1-1V3c.001-.468-.406-.99-1-1h-2V1M2 15V2h11v13H2m14 3H5v-.995L14 17c.5.005.976-.428 1-1l.021-12H16v14"&gt;&lt;/path&gt;&lt;/svg&gt;'
></button>
</div>

<div>
<h2>Button with icon and text</h2>
<br />
<button
class="CoveoActionButton"
data-title="The Text"
data-icon='&lt;svg width=".5em" height=".5em" viewBox="0 0 20 20"&gt;&lt;path d="M4 5h7v1H4V5m0 3h7v1H4V8m0 3h7v1H4v-1"&gt;&lt;/path&gt;&lt;path d="M15 1c.009-.525.066-1-1-1H1.002c-.651 0-1 .33-1 1v15c0 .66.351 1 1 1H3v2c.075.546.383 1 1 1h13c.718 0 1-.295 1-1V3c.001-.468-.406-.99-1-1h-2V1M2 15V2h11v13H2m14 3H5v-.995L14 17c.5.005.976-.428 1-1l.021-12H16v14"&gt;&lt;/path&gt;&lt;/svg&gt;'
></button>
</div>

<div>
<h2>Button with text only</h2>
<br />
<button class="CoveoActionButton" data-title="The Text"></button>
</div>

<div>
<h2>Multiple buttons</h2>
<br />

<div style="display: flex;">
<button class="CoveoActionButton" data-title="First"></button>
<button class="CoveoActionButton" data-title="Second"></button>
<button class="CoveoActionButton" data-title="Third"></button>
</div>
</div>
</body>
</html>
1 change: 1 addition & 0 deletions src/Index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// This entry point defines all the components that are included in the extensions.

export { ActionButton } from './components/ActionButton/ActionButton';
export { AttachResult } from './components/AttachResult/AttachResult';
export { UserActivity } from './components/UserActions/UserActivity';
export { UserActions } from './components/UserActions/UserActions';
Expand Down
62 changes: 62 additions & 0 deletions src/components/ActionButton/ActionButton.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
@import '../../sass/Variables.scss';

$primary-color-lightest: #ffffff;
$primary-color-lightest-hover: whitesmoke;
$primary-color-light: #e5e5e5;
$primary-color-dark: #4a4a4a;
$accent-color: $calypso;

$button-size: 36px;

button.CoveoActionButton.coveo-actionbutton {
height: $button-size;
border: solid 2px $primary-color-light;
border-radius: 3px;
background-color: $primary-color-lightest;
color: $primary-color-dark;
display: flex;
align-items: center;
justify-content: center;
white-space: nowrap;
}

.CoveoActionButton {
&:not(.coveo-actionbutton-icononly) {
padding: 0 16px;
}

&.coveo-actionbutton-icononly {
width: $button-size;
}

.coveo-actionbutton_icon {
line-height: 0;
font-size: 16px;

svg {
height: 1em;
width: 1em;
fill: $primary-color-dark;
}

+ .coveo-actionbutton_title {
margin-left: 8px;
}
}

+ .coveo-actionbutton {
margin-left: 5px;
}
}

.CoveoActionButton.coveo-actionbutton:hover,
.CoveoActionButton.coveo-actionbutton:active {
& {
color: $accent-color;
background-color: $primary-color-lightest-hover;
}

.coveo-actionbutton_icon svg {
fill: $accent-color;
}
}
143 changes: 143 additions & 0 deletions src/components/ActionButton/ActionButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { Component, ComponentOptions, IResultsComponentBindings, Initialization } from 'coveo-search-ui';

export interface IActionButtonOptions {
title?: string;
tooltip?: string;
icon?: string;
click?: () => void;
}

/**
* The _ActionButton_ component is a simple button allowing to show an icon, text, and tooltip.
*
* ```html
* <button class='CoveoActionButton'></button>
* ```
*/
export class ActionButton extends Component {
static ID = 'ActionButton';

/**
* The possible options for _ActionButton_.
* @componentOptions
*/
static options: IActionButtonOptions = {
/**
* Specifies the button label. The text is displayed on a single line, next to the icon.
*
* Default is the empty string.
*
* ```html
* <button class='CoveoActionButton' data-title='My Button'></button>
* ```
*/
title: ComponentOptions.buildStringOption(),

/**
* Specifies the button tooltip text.
*
* Default is the empty string.
*
* ```html
* <button class='CoveoActionButton' data-tooltip='My button tooltip'></button>
* ```
*/
tooltip: ComponentOptions.buildStringOption(),

/**
* Specifies the button SVG icon.
* Note: The SVG markup has to be HTML encoded when set using the HTML attributes.
*
* Default is the empty string.
*
* For example, with this SVG markup:
*
* ```xml
* <svg width="1em" height="1em">...</svg>
* ```
*
* The attribute would be set like this:
*
* ```html
* <button class='CoveoActionButton' data-icon='&lt;svg width=&quot;1em&quot; height=&quot;1em&quot;&gt;...&lt;/svg&gt;'></button>
* ```
*/
icon: ComponentOptions.buildStringOption(),

/**
* Specifies the handler called when the button is clicked.
*
* Default is `null`.
*
* This option must be set in JavaScript when initializing the component.
*/
click: ComponentOptions.buildCustomOption(s => null, { required: true })
};

constructor(public element: HTMLElement, public options: IActionButtonOptions, public bindings?: IResultsComponentBindings) {
super(element, ActionButton.ID, bindings);

this.options = ComponentOptions.initComponentOptions(element, ActionButton, options);

if (this.options.icon || this.options.title) {
this.render();
} else {
console.warn('The action button cannot render since no icon nor title is defined.');
Coveo.$$(this.element).hide();
}

if (this.options.click) {
Coveo.$$(element).on('click', () => this.options.click());
}
}

protected render(): void {
this.applyButtonStyles();

if (this.options.icon) {
this.appendIcon();
}
if (this.options.title) {
this.appendTitle();
}
if (this.options.tooltip) {
this.appendTooltip();
}
}

protected applyButtonStyles(): void {
this.element.classList.add('coveo-actionbutton');

if (this.options.icon && !this.options.title) {
this.element.classList.add('coveo-actionbutton-icononly');
}
}

protected createIconElement(): HTMLElement {
const iconElement = document.createElement('span');
iconElement.classList.add('coveo-icon', 'coveo-actionbutton_icon');
iconElement.innerHTML = this.options.icon;
return iconElement;
}

protected createTitleElement(): HTMLElement {
const titleElement = document.createElement('span');
titleElement.classList.add('coveo-actionbutton_title');
titleElement.innerText = this.options.title;
return titleElement;
}

private appendIcon(): void {
this.element.appendChild(this.createIconElement());
}

private appendTitle(): void {
this.element.appendChild(this.createTitleElement());
}

private appendTooltip(): void {
this.element.title = this.options.tooltip;
}
}

Initialization.registerAutoCreateComponent(ActionButton);
1 change: 1 addition & 0 deletions src/sass/Index.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@import '../components/ActionButton/ActionButton.scss';
@import '../components/AttachResult/AttachResult.scss';
@import '../components/UserActions/UserActions.scss';
@import '../components/ViewedByCustomer/ViewedByCustomer.scss';
Expand Down
Loading

0 comments on commit 03a1a47

Please sign in to comment.