Skip to content
This repository has been archived by the owner on Jun 29, 2023. It is now read-only.

Commit

Permalink
feat(fab): Floating Action Button (FAB) aka FABULOUS!!! component. (#824
Browse files Browse the repository at this point in the history
)

* FAB WIP

* fab

* cleanup

* cleanup

* cleanup

* cleanup

* usage, story, tests, readme, fix bg color

* Fab styling and demos.

* review fixes

* remove dependency

* feat: props and style for fab

* Added appearance, scale, and icon props.

* Minor style rule. e2e updates. Readme updates.

* Added knobs for new FAB props.

* import CSS object for .button

* I FORGOT THE DOT!

* Cleanup.

* fix test maybe

Co-authored-by: Alan Sangma <asangma@esri.com>
  • Loading branch information
driskull and asangma authored Feb 27, 2020
1 parent dec10b1 commit fc8de45
Show file tree
Hide file tree
Showing 22 changed files with 451 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .storybook/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const dirOptions: Direction[] = ["ltr", "rtl"];
const themeOptions: CalciteTheme[] = ["light", "dark"];
const positionOptions: CalcitePosition[] = ["start", "end"];
const scaleOptions: CalciteScale[] = ["s", "m", "l"];
const appearanceOptions: CalciteAppearance[] = ["solid", "clear"];
const appearanceOptions: CalciteAppearance[] = ["solid", "clear", "outline"];

export const ATTRIBUTES: CommonAttributes = {
dir: {
Expand Down
14 changes: 10 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"sortablejs": "1.10.2"
},
"devDependencies": {
"@esri/calcite-components": "1.0.0-beta.18",
"@esri/calcite-components": "1.0.0-beta.19",
"@stencil/core": "1.9.0-18",
"@stencil/postcss": "1.0.1",
"@stencil/sass": "1.1.1",
Expand Down Expand Up @@ -104,7 +104,7 @@
"updtr": "3.1.0"
},
"peerDependencies": {
"@esri/calcite-components": "1.0.0-beta.16"
"@esri/calcite-components": "1.0.0-beta.19"
},
"license": "Apache-2.0"
}
2 changes: 1 addition & 1 deletion src/components/calcite-action/calcite-action.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class CalciteAction {

// --------------------------------------------------------------------------
//
// Variables
// Private Properties
//
// --------------------------------------------------------------------------

Expand Down
59 changes: 59 additions & 0 deletions src/components/calcite-fab/calcite-fab.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, hidden, renders } from "../../tests/commonTests";
import { CSS } from "./resources";

describe("calcite-fab", () => {
it("renders", async () => renders("calcite-fab"));

it("honors hidden attribute", async () => hidden("calcite-fab"));

it("should have visible text when text is enabled", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-fab text="hello world" text-enabled></calcite-fab>`);

const button = await page.find(`calcite-fab >>> .${CSS.button}`);
const text = button.textContent;

expect(text).toBe("hello world");
});

it("should not have visible text when text is not enabled", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-fab text="hello world"></calcite-fab>`);

const button = await page.find(`calcite-fab >>> .${CSS.button}`);
const text = button.textContent;

expect(text).toBe("");
});

it("should have label", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-fab text="hello world" label="hi"></calcite-fab>`);

const button = await page.find(`calcite-fab >>> .${CSS.button}`);
expect(button.getAttribute("title")).toBe("hi");
expect(button.getAttribute("aria-label")).toBe("hi");
});

it("should be disabled", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-fab disabled></calcite-fab>`);

const button = await page.find(`calcite-fab >>> .${CSS.button}`);
expect(button).toHaveAttribute("disabled");
});

it("should have appearance=outline", async () => {
const page = await newE2EPage();
await page.setContent(`<calcite-fab text="hello world"></calcite-fab>`);

const fab = await page.find(`calcite-fab >>> .${CSS.button}`);
expect(fab.getAttribute("appearance")).toBe("outline");
});

it("should be accessible", async () => {
await accessible(`<calcite-fab text="hello world"></calcite-fab>`);
await accessible(`<calcite-fab text="hello world" disabled text-enabled></calcite-fab>`);
});
});
6 changes: 6 additions & 0 deletions src/components/calcite-fab/calcite-fab.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
:host {
@extend %component-host;
background-color: var(--calcite-app-background-transparent);
}

@import "../../scss/includes/_component";
69 changes: 69 additions & 0 deletions src/components/calcite-fab/calcite-fab.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { boolean, select, text, withKnobs } from "@storybook/addon-knobs";
import {
Attributes,
createComponentHTML as create,
darkBackground,
parseReadme,
titlelessDocsPage
} from "../../../.storybook/utils";
import readme from "./readme.md";
import { ATTRIBUTES } from "../../../.storybook/resources";
import { ICONS } from "./resources";
const { appearance, dir, scale, theme } = ATTRIBUTES;

export default {
title: "components|calcite-fab",
decorators: [withKnobs],
parameters: {
backgrounds: darkBackground,
docs: {
page: titlelessDocsPage
},
notes: parseReadme(readme)
}
};

const createAttributes: () => Attributes = () => [
{
name: "appearance",
value: select("appearance", appearance.values, appearance.values[2])
},
{
name: "dir",
value: select("dir", dir.values, dir.defaultValue)
},
{
name: "disabled",
value: boolean("disabled", false)
},
{
name: "icon",
value: text("icon", ICONS.plus)
},
{
name: "label",
value: text("label", "Label")
},
{
name: "loading",
value: boolean("loading", false)
},
{
name: "text",
value: text("text", "Text")
},
{
name: "text-enabled",
value: boolean("textEnabled", false)
},
{
name: "scale",
value: select("scale", scale.values, scale.defaultValue)
},
{
name: "theme",
value: select("theme", theme.values, theme.defaultValue)
}
];

export const basic = () => create("calcite-fab", createAttributes(), `<calcite-fab></calcite-fab>`);
131 changes: 131 additions & 0 deletions src/components/calcite-fab/calcite-fab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { Component, Element, Host, Method, Prop, h } from "@stencil/core";
import { CalciteAppearance, CalciteScale, CalciteTheme } from "../interfaces";
import { CSS, ICONS } from "./resources";
import { focusElement, getElementDir } from "../utils/dom";

@Component({
tag: "calcite-fab",
styleUrl: "calcite-fab.scss",
shadow: true
})
export class CalciteFab {
// --------------------------------------------------------------------------
//
// Properties
//
// --------------------------------------------------------------------------

/**
* Used to set the button's appearance. Default is outline.
*/
@Prop({ reflect: true }) appearance: CalciteAppearance = "outline";

/**
* When true, disabled prevents interaction. This state shows items with lower opacity/grayed.
*/
@Prop({ reflect: true }) disabled = false;

/**
* The name of the icon to display. The value of this property must match the icon name from https://esri.github.io/calcite-ui-icons/.
*/
@Prop() icon?: string = ICONS.plus;

/**
* Label of the FAB, exposed on hover. If no label is provided, the label inherits what's provided for the `text` prop.
*/
@Prop() label?: string;

/**
* When true, content is waiting to be loaded. This state shows a busy indicator.
*/
@Prop({ reflect: true }) loading = false;

/**
* Specifies the size of the fab.
*/
@Prop({ reflect: true }) scale: CalciteScale = "m";

/**
* Text that accompanies the FAB icon.
*/
@Prop() text?: string;

/**
* Indicates whether the text is displayed.
*/
@Prop({ reflect: true }) textEnabled = false;

/**
* Used to set the component's color scheme.
*/
@Prop({ reflect: true }) theme: CalciteTheme;

// --------------------------------------------------------------------------
//
// Private Properties
//
// --------------------------------------------------------------------------

@Element() el: HTMLCalciteFabElement;

private buttonEl: HTMLElement;

// --------------------------------------------------------------------------
//
// Methods
//
// --------------------------------------------------------------------------

@Method()
async setFocus() {
focusElement(this.buttonEl);
}

// --------------------------------------------------------------------------
//
// Render Methods
//
// --------------------------------------------------------------------------

render() {
const {
appearance,
disabled,
el,
loading,
scale,
theme,
textEnabled,
icon,
label,
text
} = this;
const titleText = !textEnabled && text;
const title = label || titleText;
const dir = getElementDir(el);

return (
<Host>
<calcite-button
class={CSS.button}
loading={loading}
disabled={disabled}
title={title}
aria-label={label}
theme={theme}
dir={dir}
scale={scale}
icon={icon}
round={true}
floating={true}
width="auto"
appearance={appearance}
color="blue"
ref={(buttonEl) => (this.buttonEl = buttonEl)}
>
{this.textEnabled ? this.text : null}
</calcite-button>
</Host>
);
}
}
57 changes: 57 additions & 0 deletions src/components/calcite-fab/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# calcite-fab

<!-- Auto Generated Below -->

## Usage

### Basic

#### Without text

Renders a `calcite-fab` that displays only an icon and a tooltip label.

```html
<calcite-fab label="Performs my custom action"></calcite-fab>
```

#### With text

Renders a `calcite-fab` that displays text along side an icon and a tooltip label.

```html
<calcite-fab label="Performs my custom action" text="Perform Action!" text-enabled></calcite-fab>
```

#### Loading and disabled

Renders a `calcite-fab` that is loading and disabled.

```html
<calcite-fab loading disabled></calcite-fab>
```

## Properties

| Property | Attribute | Description | Type | Default |
| ------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ----------- |
| `appearance` | `appearance` | Used to set the button's appearance. Default is outline. | `CalciteAppearance` | `outline` |
| `disabled` | `disabled` | When true, disabled prevents interaction. This state shows items with lower opacity/grayed. | `boolean` | `false` |
| `icon` | `icon` | The name of the icon to display. The value of this property must match the icon name from https://esri.github.io/calcite-ui-icons/. | `string` | `plus` |
| `label` | `label` | Label of the fab, exposed on hover. If no label is provided, the label inherits what's provided for the `text` prop. | `string` | `undefined` |
| `loading` | `loading` | When true, content is waiting to be loaded. This state shows a busy indicator. | `boolean` | `false` |
| `scale` | `scale` | Specifies the size of the fab. | `CalciteScale` | `m` |
| `text` | `text` | Text that accompanies the fab icon. | `string` | `undefined` |
| `textEnabled` | `text-enabled` | Indicates whether the text is displayed. | `boolean` | `false` |
| `theme` | `theme` | Used to set the component's color scheme. | `"dark" or "light"` | `undefined` |

## Methods

### `setFocus() => Promise<void>`

#### Returns

Type: `Promise<void>`

---

_Built with [StencilJS](https://stenciljs.com/)_
Loading

0 comments on commit fc8de45

Please sign in to comment.