Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support primary and accent variants for toggle and menu buttons #1934

Merged
merged 18 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
m-akinc marked this conversation as resolved.
Show resolved Hide resolved
"type": "major",
"comment": "Support primary and accent variants for toggle and menu buttons. BREAKING CHANGE: Removed theme-aware tokens `buttonFillActivePrimaryColor` and `buttonFillAccentActiveColor`. Use `fillSelectedColor` instead.",
"packageName": "@ni/nimble-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix value of DigitalGreenDark",
"packageName": "@ni/nimble-tokens",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import type { StoryFn, Meta } from '@storybook/html';
import { html, ViewTemplate, when } from '@microsoft/fast-element';
import { pascalCase } from '@microsoft/fast-web-utilities';
import {
ButtonAppearance,
ButtonAppearanceVariant
} from '../../patterns/button/types';
import {
createMatrix,
sharedMatrixParameters,
Expand All @@ -17,6 +12,14 @@ import { textCustomizationWrapper } from '../../utilities/tests/text-customizati
import { anchorButtonTag } from '..';
import { iconLinkTag } from '../../icons/link';
import { iconArrowExpanderRightTag } from '../../icons/arrow-expander-right';
import {
appearanceStates,
type AppearanceState,
type AppearanceVariantState,
type PartVisibilityState,
appearanceVariantStates,
partVisibilityStates
} from '../../patterns/button/tests/states';

const metadata: Meta = {
title: 'Tests/Anchor Button',
Expand All @@ -27,26 +30,6 @@ const metadata: Meta = {

export default metadata;

/* array of iconVisible, labelVisible, endIconVisible */
const partVisibilityStates = [
[true, true, false],
[true, false, false],
[false, true, false],
[true, true, true],
[false, true, true]
] as const;
type PartVisibilityState = (typeof partVisibilityStates)[number];

const appearanceStates: [string, string | undefined][] = Object.entries(
ButtonAppearance
).map(([key, value]) => [pascalCase(key), value]);
type AppearanceState = (typeof appearanceStates)[number];

const appearanceVariantStates: [string, string | undefined][] = Object.entries(
ButtonAppearanceVariant
).map(([key, value]) => [pascalCase(key), value]);
type AppearanceVariantState = (typeof appearanceVariantStates)[number];

// prettier-ignore
const component = (
[disabledName, disabled]: DisabledState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
} from '@storybook/blocks';
import TargetDocs from '../../patterns/anchor/tests/target-docs.mdx';
import ContentHiddenDocs from '../../patterns/button/tests/content-hidden-docs.mdx';
import StylingDocs from '../../patterns/button/tests/styling-docs.mdx';
import { anchorButtonTag } from '..';
import * as anchorButtonStories from './anchor-button.stories';

<Meta of={anchorButtonStories} />
Expand All @@ -18,9 +20,7 @@ An anchor button is a component with the visual appearance of a button, but it n
<Canvas of={anchorButtonStories.outlineAnchorButton} />
<Controls of={anchorButtonStories.outlineAnchorButton} />

{/* ## Appearances */}

{/* ## Appearance Variants */}
<StylingDocs components={{ Button: anchorButtonTag }} />

{/* ## Usage */}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { StoryFn, Meta } from '@storybook/html';
import { html, ViewTemplate, when } from '@microsoft/fast-element';
import { pascalCase } from '@microsoft/fast-web-utilities';
import { ButtonAppearance, ButtonAppearanceVariant } from '../types';
import {
createMatrix,
sharedMatrixParameters,
Expand All @@ -15,6 +13,14 @@ import { buttonTag } from '..';
import { iconKeyTag } from '../../icons/key';
import { iconArrowExpanderDownTag } from '../../icons/arrow-expander-down';
import { bodyFont } from '../../theme-provider/design-tokens';
import {
appearanceStates,
type AppearanceState,
type AppearanceVariantState,
type PartVisibilityState,
appearanceVariantStates,
partVisibilityStates
} from '../../patterns/button/tests/states';

const metadata: Meta = {
title: 'Tests/Button',
Expand All @@ -25,26 +31,6 @@ const metadata: Meta = {

export default metadata;

/* array of iconVisible, labelVisible, endIconVisible */
const partVisibilityStates = [
[true, true, false],
[true, false, false],
[false, true, false],
[true, true, true],
[false, true, true]
] as const;
type PartVisibilityState = (typeof partVisibilityStates)[number];

const appearanceStates: [string, string | undefined][] = Object.entries(
ButtonAppearance
).map(([key, value]) => [pascalCase(key), value]);
type AppearanceState = (typeof appearanceStates)[number];

const appearanceVariantStates: [string, string | undefined][] = Object.entries(
ButtonAppearanceVariant
).map(([key, value]) => [pascalCase(key), value]);
type AppearanceVariantState = (typeof appearanceVariantStates)[number];

// prettier-ignore
const component = (
[disabledName, disabled]: DisabledState,
Expand Down
202 changes: 2 additions & 200 deletions packages/nimble-components/src/button/tests/button.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Canvas, Meta, Controls, Title } from '@storybook/blocks';
import ContentHiddenDocs from '../../patterns/button/tests/content-hidden-docs.mdx';
import { NimbleButton } from './button.react';
import { NimbleIconKey } from '../../icons/tests/key.react';
import StylingDocs from '../../patterns/button/tests/styling-docs.mdx';
import { buttonTag } from '..';
import { anchorButtonTag } from '../../anchor-button';
import * as buttonStories from './button.stories';
Expand All @@ -18,209 +17,12 @@ If you want a button that triggers navigation to a URL, use the <Tag name={ancho
<Canvas of={buttonStories.outlineButton} />
<Controls of={buttonStories.outlineButton} />

## Styling

### Appearances

These appearances have the default styling of the <Tag name={buttonTag}/>. Each should be considered for use before using appearance variant buttons.

#### Ghost Button:

<Frame>
<Container>
<Column stylingClass="controls">
<NimbleButton appearance="ghost">{`Ghost Button`}</NimbleButton>
<NimbleButton appearance="ghost" content-hidden>
<NimbleIconKey slot="start"></NimbleIconKey>
{`Ghost Button`}
</NimbleButton>

</Column>
<Column>
<ul>
<li>Ghost is the default appearance and should be the first considered for use.</li>
<li>
Use as the default and standard option to create a clean airy and open UI
feel. Ghost buttons fit comfortably in tight spaces and help control the
visual density of the UI.
</li>
<li>
Be careful when using that the surrounding context does not cause this
button to be confused for emphasized body text, tabs or a standalone links.
</li>
<li>Use in combination with a primary outline or primary block buttons to create a hierarchy of importance. There is no primary ghost button.</li>
</ul>
</Column>
</Container>

</Frame>

#### Outline Button:

<Frame>
<Container>
<Column stylingClass="controls">
<NimbleButton appearance="outline">{`Outline Button`}</NimbleButton>
<NimbleButton appearance="outline" content-hidden>
<NimbleIconKey slot="start"></NimbleIconKey>
{`Outline Button`}
</NimbleButton>
</Column>
<Column>
<ul>
<li>
Outline is the secondary style and should be considered for
use when ghost button is not sufficient.
</li>
<li>
Use as an alternative standard button when a ghost button is
not suitable. Use like a ghost button to create a clean,
light and airy feel.
</li>
<li>
The outline button is more visually direct about the
control's functionality than a ghost button.
</li>
<li>
Use in combination with ghost buttons (but not block
buttons) to create hierarchy.
</li>
</ul>
</Column>
</Container>
</Frame>

#### Block Button:

<Frame>
<Container>
<Column stylingClass="controls">
<NimbleButton appearance="block">{`Block Button`}</NimbleButton>
<NimbleButton appearance="block" content-hidden>
<NimbleIconKey slot="start"></NimbleIconKey>
{`Block Button`}
</NimbleButton>
</Column>
<Column>
<ul>
<li>
Block is the tertiary style used for creating the most eye
catching and functionally direct button. Use in areas where
controls are not often present or obvious, or when lots of
busy information can cause an important control to be
overlooked.
</li>
<li>
Use as a standard button when the most visible solution is
required. Use as an alternative to overly subtle button
solutions when it is important to emphasize an action and
the functionality of the control.
</li>
<li>
Use in combination with ghost buttons (but not outline
buttons) to create hierarchy.
</li>
</ul>
</Column>
</Container>
</Frame>

### Appearance Variants

Button appearance variants are mainly used when a button needs be distinguished for one of the following reasons:

- To indicate the action that allows the user to accomplish their most common or important goal
- To indicate the action that allows the user to complete their task

There are currently two available values for `appearance-variant`: `primary` and `accent`.

#### Accent Button:

<Frame>
<Container config="325px 1fr">
<Column stylingClass="controls">
<NimbleButton appearance="ghost">{`Ghost`}</NimbleButton>
<NimbleButton appearance="ghost">{`Ghost`}</NimbleButton>
<NimbleButton
appearance="outline"
appearance-variant="accent"
>{`Outline Accent`}</NimbleButton>
<Divider />
<NimbleButton appearance="ghost">{`Ghost`}</NimbleButton>
<NimbleButton appearance="ghost">{`Ghost`}</NimbleButton>
<NimbleButton
appearance="block"
appearance-variant="accent"
>{`Block Accent`}</NimbleButton>
</Column>
<Column>
<Do>
Use only 1 accent button in a section. It should be used when
trying to achieve the most prominent eye-catching approach.
Consider the contextual implications of using a green colored
button in relation to its name or metaphor. Remember that color
has accessibility constraints and the color alone should not be
relied on.
</Do>
<Do>
Use in situations that lack color and enthusiasm to help support
the brand.
</Do>
<Do>Use in combination with ghost buttons to create hierarchy.</Do>
<Dont>
Do not use an outline button with a block button to create
hierarchy.
</Dont>
</Column>
</Container>
</Frame>

#### Primary Button:

<Frame>
<Container config="325px 1fr">
<Column stylingClass="controls">
<NimbleButton appearance="ghost">{`Ghost`}</NimbleButton>
<NimbleButton appearance="ghost">{`Ghost`}</NimbleButton>
<NimbleButton appearance="outline" appearance-variant="primary">
{`Outline Primary`}
</NimbleButton>
<Divider/>
<NimbleButton appearance="ghost">{`Ghost`}</NimbleButton>
<NimbleButton appearance="ghost">{`Ghost`}</NimbleButton>
<NimbleButton appearance="block" appearance-variant="primary">
{`Block Primary`}
</NimbleButton>
</Column>
<Column>
<Do>
Use only 1 primary button in a section. It should be used when
there is a conflict with color and its context.
</Do>
<Do>Use in combination with ghost buttons to create hierarchy.</Do>
<Dont>
Do not use an outline button with a block button to create
hierarchy.
</Dont>
</Column>
</Container>

</Frame>

#### Examples:

To see more examples of appearance variant button hierarchy, see the nimble-button Figma docs for [Primary and Standard Actions](https://www.figma.com/file/PO9mFOu5BCl8aJvFchEeuN/Nimble_Components?type=design&node-id=1604-74603&mode=design&t=SQ3lyK83VHBUaOkg-0).
<StylingDocs components={{ Button: buttonTag }} />

## Sizing

Nimble Buttons are currently always 32px tall. Designs exist for other sizes; if you need these in an application, please comment on [Configurable height for nimble controls (#610)](https://github.com/ni/nimble/issues/610).

## Styling / Theme

Use the Color UI version for backgrounds with color (e.g. purple, blue).

See the usage details for more information on button styling / usage.

## Accessibility

Please work with your designer and ensure you have a 4.5:1
Expand Down
11 changes: 9 additions & 2 deletions packages/nimble-components/src/menu-button/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import {
keyArrowUp,
keyEscape
} from '@microsoft/fast-web-utilities';
import { ButtonAppearance } from '../button/types';
import {
ButtonAppearance,
ButtonAppearanceVariant,
MenuButtonToggleEventDetail,
MenuButtonPosition
} from './types';
import type { ToggleButton } from '../toggle-button';
import { styles } from './styles';
import { template } from './template';
import { MenuButtonToggleEventDetail, MenuButtonPosition } from './types';
import type { ButtonPattern } from '../patterns/button/types';
import type { AnchoredRegion } from '../anchored-region';

Expand All @@ -27,6 +31,9 @@ export class MenuButton extends FoundationElement implements ButtonPattern {
@attr
public appearance: ButtonAppearance = ButtonAppearance.outline;

@attr({ attribute: 'appearance-variant' })
public appearanceVariant: ButtonAppearanceVariant;

@attr({ mode: 'boolean' })
public disabled = false;

Expand Down
1 change: 1 addition & 0 deletions packages/nimble-components/src/menu-button/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const template = html<MenuButton>`
<${toggleButtonTag}
part="button"
appearance="${x => x.appearance}"
appearance-variant="${x => x.appearanceVariant}"
?content-hidden="${x => x.contentHidden}"
?checked="${x => x.open}"
?disabled="${x => x.disabled}"
Expand Down
Loading
Loading