Skip to content

Commit

Permalink
feat(toolbar): add Toolbar, ToolbarButton, and ToolbarGroup com…
Browse files Browse the repository at this point in the history
…ponents (#1103)

* feat(toolbar): add `Toolbar`, `ToolbarButton`, and `ToolbarGroup`

* build(toolbar): add `ToolbarButton`, and `ToolbarGroup` to canary

* docs(storybook): use Story helpers for naming

* chore: merge `main` into `67/toolbar`

* chore: revert `REVIEWERS_GUIDELINES.md`

* fix(toolbar): add border style

* chore(toolbar): update styles, story

* test(toolbar): add tests to validate props

* test(toolbar): add shared specs

* test(toolbar): add shared specs

* docs(toolbar): add `Dropdown` to example

* docs(toolbar): fix Storybook order

Co-authored-by: Dave Clark <[email protected]>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 17, 2021
1 parent dacc67b commit 2d5f4cf
Show file tree
Hide file tree
Showing 13 changed files with 357 additions and 1 deletion.
43 changes: 43 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/Toolbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright IBM Corp. 2021, 2021
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import cx from 'classnames';
import { node, string } from 'prop-types';
import React, { forwardRef } from 'react';

import { pkg } from '../../settings';

const { checkComponentEnabled, prefix } = pkg;
const blockClass = `${prefix}--toolbar`;

/** Toolbars are a collection of action items that organize a program’s interaction patterns into a series of closely related commands. */
let Toolbar = forwardRef(({ children, className, ...rest }, ref) => {
return (
<div
{...rest}
ref={ref}
className={cx(blockClass, className)}
role="toolbar">
{children}
</div>
);
});

const componentName = 'Toolbar';
Toolbar.displayName = componentName;

Toolbar.propTypes = {
/** Provide the content of the `Toolbar` */
children: node.isRequired,

/** Provide an optional class to be applied to the containing node */
className: string,
};

Toolbar = checkComponentEnabled(Toolbar, componentName);

export { blockClass, Toolbar };
26 changes: 26 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/Toolbar.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ArgsTable, Preview, Story } from '@storybook/addon-docs/blocks';

import { getStorybookSlug } from '../../../config';
import { pkg } from '../../settings';

import { Toolbar } from '.';

# Toolbar

## Table of Contents

- [Overview](#overview)
- [Component API](#component-api)

## Overview

A toolbar is a collection of action items that organizes a program’s interaction
patterns into a series of closely related commands.

<Preview>
<Story id={getStorybookSlug(pkg, Toolbar.displayName, 'toolbar')} />
</Preview>

## Component API

<ArgsTable />
95 changes: 95 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/Toolbar.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* Copyright IBM Corp. 2021, 2021
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import {
AlignHorizontalCenter16,
Minimize16,
Printer16,
Redo16,
Save16,
Share16,
Undo16,
Upload16,
ZoomIn16,
ZoomOut16,
} from '@carbon/icons-react';

import {
Dropdown,
OverflowMenu,
OverflowMenuItem,
} from 'carbon-components-react';

import React, { useState } from 'react';

import { getStoryTitle } from '../../global/js/utils/story-helper';

import { Toolbar, ToolbarButton, ToolbarGroup } from '../..';
import mdx from './Toolbar.mdx';

export default {
title: getStoryTitle(Toolbar.displayName),
component: Toolbar,
subcomponents: { ToolbarGroup, ToolbarButton },

parameters: {
docs: {
page: mdx,
},
},
};

export function _Toolbar(args) {
const dropdownItems = ['11', '12', '14', '16', '18'];

const [selectedDropdownItem, setSelectedDropdownItem] = useState(
dropdownItems[(dropdownItems.length / 2) | 0]
);

return (
<Toolbar {...args}>
<ToolbarGroup>
<ToolbarButton iconDescription="Save" renderIcon={Save16} />
<ToolbarButton iconDescription="Share" renderIcon={Share16} />
<ToolbarButton iconDescription="Upload" renderIcon={Upload16} />
<ToolbarButton iconDescription="Print" renderIcon={Printer16} />
</ToolbarGroup>

<ToolbarGroup>
<ToolbarButton iconDescription="Undo" renderIcon={Undo16} />
<ToolbarButton iconDescription="Redo" renderIcon={Redo16} />
<ToolbarButton iconDescription="Zoom in" renderIcon={ZoomIn16} />
<ToolbarButton iconDescription="Zoom out" renderIcon={ZoomOut16} />
<ToolbarButton iconDescription="Minimize" renderIcon={Minimize16} />

<ToolbarButton
iconDescription="Align horizontal center"
renderIcon={AlignHorizontalCenter16}
/>
</ToolbarGroup>

<ToolbarGroup>
<Dropdown
id="dropdown"
initialSelectedItem={selectedDropdownItem}
items={dropdownItems}
label={selectedDropdownItem}
onChange={({ selectedItem }) => setSelectedDropdownItem(selectedItem)}
/>
</ToolbarGroup>

<ToolbarGroup>
<OverflowMenu flipped>
<OverflowMenuItem itemText="Color palette" />
<OverflowMenuItem itemText="Text creation" />
<OverflowMenuItem itemText="Bulleted list" />
<OverflowMenuItem itemText="Delete" hasDivider isDelete />
</OverflowMenu>
</ToolbarGroup>
</Toolbar>
);
}
59 changes: 59 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/Toolbar.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Copyright IBM Corp. 2021, 2021
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { render as r, screen } from '@testing-library/react';
import React, { createRef } from 'react';

import { Toolbar, ToolbarButton, ToolbarGroup } from '../..';
const { getByTestId } = screen;

function test(T) {
const { displayName } = T;

describe(displayName, () => {
const dataTestId = 'dataTestId';

function render(props) {
return r(
<T data-test-id={dataTestId} {...props}>
{displayName}
</T>
);
}

it('has no accessibility violations', async () => {
const { container } = render();

await expect(container).toBeAccessible(displayName);
await expect(container).toHaveNoAxeViolations();
});

it('adds a class to the containing node', () => {
const className = 'class-name';
render({ className });

expect(getByTestId(dataTestId)).toHaveClass(className);
});

it('adds additional props to the containing node', () => {
render();

getByTestId(dataTestId);
});

it('forwards a reference to the appropriate DOM node', () => {
const ref = createRef();
render({ ref });

expect(getByTestId(dataTestId)).toEqual(ref.current);
});
});
}

test(Toolbar);
test(ToolbarButton);
test(ToolbarGroup);
21 changes: 21 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/ToolbarButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright IBM Corp. 2021, 2021
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { Button } from 'carbon-components-react';
import React, { forwardRef } from 'react';

import { pkg } from '../../settings';

/** Toolbar buttons are common functions performed as part of a products interface or an open window. */
export let ToolbarButton = forwardRef((props, ref) => {
return <Button {...props} ref={ref} kind="ghost" size="md" hasIconOnly />;
});

const componentName = 'ToolbarButton';
ToolbarButton.displayName = componentName;

ToolbarButton = pkg.checkComponentEnabled(ToolbarButton, componentName);
40 changes: 40 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/ToolbarGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright IBM Corp. 2021, 2021
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import cx from 'classnames';
import { node, string } from 'prop-types';
import React, { forwardRef } from 'react';

import { pkg } from '../../settings';
import { blockClass } from './Toolbar';

/** Toolbar groups organize the commands within a toolbar into related groups. */
export let ToolbarGroup = forwardRef(
({ className, children, ...rest }, ref) => {
return (
<div
{...rest}
ref={ref}
className={cx(`${blockClass}__group`, className)}>
{children}
</div>
);
}
);

const componentName = 'ToolbarGroup';
ToolbarGroup.displayName = componentName;

ToolbarGroup.propTypes = {
/** Provide the content of the `ToolbarGroup` */
children: node.isRequired,

/** Provide an optional class to be applied to the containing node */
className: string,
};

ToolbarGroup = pkg.checkComponentEnabled(ToolbarGroup, componentName);
8 changes: 8 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//
// Copyright IBM Corp. 2021, 2021
//
// This source code is licensed under the Apache-2.0 license found in the
// LICENSE file in the root directory of this source tree.
//

@import './toolbar';
48 changes: 48 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/_toolbar.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// Copyright IBM Corp. 2021, 2021
//
// This source code is licensed under the Apache-2.0 license found in the
// LICENSE file in the root directory of this source tree.
//

@import '@carbon/import-once/scss/import-once';
@import '@carbon/layout/scss/breakpoint';
@import '@carbon/layout/scss/convert';
@import '@carbon/themes/scss/tokens';

@import 'carbon-components/scss/components/button/button';
@import 'carbon-components/scss/globals/scss/vars';

@import '../../global/styles/project-settings';

@include exports($name: cloud-cognitive--toolbar) {
$block-class: #{$pkg-prefix}--toolbar;
$border: carbon--rem(
$px: 1px,
)
solid $ui-03;

.#{$block-class} {
@include carbon--breakpoint($name: lg) {
justify-content: flex-end;
}

display: flex;
min-height: carbon--rem($px: 40px);
border-bottom: $border;
background-color: $ui-01;
}

.#{$block-class} .#{$prefix}--dropdown {
border-bottom-width: 0;
}

.#{$block-class}__group {
display: flex;
border-right: $border;
}

.#{$block-class}__group:last-of-type {
border-right-width: 0;
}
}
10 changes: 10 additions & 0 deletions packages/cloud-cognitive/src/components/Toolbar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright IBM Corp. 2021, 2021
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

export { Toolbar } from './Toolbar';
export { ToolbarButton } from './ToolbarButton';
export { ToolbarGroup } from './ToolbarGroup';
1 change: 1 addition & 0 deletions packages/cloud-cognitive/src/components/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,6 @@
@import './StatusIcon/index';
@import './TagSet/index';
@import './Tearsheet/index';
@import './Toolbar/index';
@import './WebTerminal/index';
@import './UserProfileImage/index';
3 changes: 2 additions & 1 deletion packages/cloud-cognitive/src/components/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright IBM Corp. 2020, 2020
// Copyright IBM Corp. 2020, 2021
//
// This source code is licensed under the Apache-2.0 license found in the
// LICENSE file in the root directory of this source tree.
Expand Down Expand Up @@ -47,5 +47,6 @@ export { SidePanel } from './SidePanel';
export { StatusIcon } from './StatusIcon';
export { TagSet } from './TagSet';
export { Tearsheet, TearsheetNarrow } from './Tearsheet';
export { Toolbar, ToolbarButton, ToolbarGroup } from './Toolbar';
export { UserProfileImage } from './UserProfileImage';
export { WebTerminal } from './WebTerminal';
3 changes: 3 additions & 0 deletions packages/cloud-cognitive/src/global/js/package-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const defaults = {
ExampleComponent: false,
LoadingBar: false,
ModifiedTabs: false,
Toolbar: false,
ToolbarButton: false,
ToolbarGroup: false,
WebTerminal: false,
/* new component flags here - comment used by generate CLI */
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const storybookStructure = {
SidePanel: `${lib}/$rci/$comp`,
StatusIcon: `${lib}/$rci/$comp`,
TagSet: `${lib}/$rci/$comp`,
Toolbar: `${lib}/$rci/$comp`,
UserProfileImage: `${lib}/$rci/$comp`,
WebTerminal: `${lib}/$rci/$comp`,

Expand Down

0 comments on commit 2d5f4cf

Please sign in to comment.