@kaizen/components 1.70.20 → 1.70.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/cjs/Filter/FilterBar/subcomponents/FilterBarButton/FilterBarButton.cjs +1 -0
  2. package/dist/cjs/Filter/FilterMultiSelect/subcomponents/Trigger/FilterTriggerButton/FilterTriggerButton.cjs +1 -0
  3. package/dist/cjs/Filter/FilterMultiSelect/subcomponents/Trigger/RemovableFilterTrigger/RemovableFilterTrigger.cjs +1 -0
  4. package/dist/cjs/RichTextEditor/RichTextEditor/utils/controlmap.cjs +1 -0
  5. package/dist/cjs/RichTextEditor/utils/commands/fixtures/mockRangeForBoundingRect.cjs +40 -0
  6. package/dist/cjs/RichTextEditor/utils/plugins/LinkManager/LinkManager.cjs +1 -0
  7. package/dist/cjs/index.cjs +4 -0
  8. package/dist/esm/Filter/FilterBar/subcomponents/FilterBarButton/FilterBarButton.mjs +1 -0
  9. package/dist/esm/Filter/FilterMultiSelect/subcomponents/Trigger/FilterTriggerButton/FilterTriggerButton.mjs +1 -0
  10. package/dist/esm/Filter/FilterMultiSelect/subcomponents/Trigger/RemovableFilterTrigger/RemovableFilterTrigger.mjs +1 -0
  11. package/dist/esm/RichTextEditor/RichTextEditor/utils/controlmap.mjs +1 -0
  12. package/dist/esm/RichTextEditor/utils/commands/fixtures/mockRangeForBoundingRect.mjs +38 -0
  13. package/dist/esm/RichTextEditor/utils/plugins/LinkManager/LinkManager.mjs +1 -0
  14. package/dist/esm/index.mjs +2 -0
  15. package/dist/types/Filter/FilterBar/subcomponents/FilterBarButton/FilterBarButton.d.ts +1 -1
  16. package/dist/types/Filter/FilterButton/index.d.ts +1 -0
  17. package/dist/types/RichTextEditor/utils/commands/fixtures/helpers.d.ts +0 -1
  18. package/dist/types/RichTextEditor/utils/commands/fixtures/index.d.ts +2 -0
  19. package/dist/types/RichTextEditor/utils/commands/fixtures/mockRangeForBoundingRect.d.ts +1 -0
  20. package/dist/types/RichTextEditor/utils/commands/index.d.ts +1 -0
  21. package/package.json +1 -1
  22. package/src/Filter/FilterButton/index.ts +1 -0
  23. package/src/RichTextEditor/utils/commands/addMark.spec.ts +1 -5
  24. package/src/RichTextEditor/utils/commands/fixtures/helpers.ts +0 -31
  25. package/src/RichTextEditor/utils/commands/fixtures/index.ts +2 -0
  26. package/src/RichTextEditor/utils/commands/fixtures/mockRangeForBoundingRect.ts +32 -0
  27. package/src/RichTextEditor/utils/commands/index.ts +1 -0
  28. package/src/__rc__/Button/_docs/Button--usage-guidelines.mdx +170 -1
  29. package/src/__rc__/Button/_docs/Button.docs.stories.tsx +264 -9
  30. package/src/__rc__/Button/_docs/assets/button_anatomy.png +0 -0
  31. package/src/__rc__/Button/_docs/assets/button_icon_only_spec.png +0 -0
  32. package/src/__rc__/Button/_docs/assets/button_icon_spec.png +0 -0
  33. package/src/__rc__/Button/_docs/assets/button_spec.png +0 -0
@@ -4,6 +4,7 @@ var tslib = require('tslib');
4
4
  var React = require('react');
5
5
  var classnames = require('classnames');
6
6
  var FilterBarContext = require('../../context/FilterBarContext.cjs');
7
+ require('../../../FilterButton/subcomponents/FilterButtonBase/FilterButtonBase.cjs');
7
8
  var FilterButton = require('../../../FilterButton/FilterButton/FilterButton.cjs');
8
9
  var FilterButtonRemovable = require('../../../FilterButton/FilterButtonRemovable/FilterButtonRemovable.cjs');
9
10
  var isRefObject = require('../../../../utils/isRefObject.cjs');
@@ -2,6 +2,7 @@
2
2
 
3
3
  var tslib = require('tslib');
4
4
  var React = require('react');
5
+ require('../../../../FilterButton/subcomponents/FilterButtonBase/FilterButtonBase.cjs');
5
6
  var FilterButton = require('../../../../FilterButton/FilterButton/FilterButton.cjs');
6
7
  require('../../../../FilterButton/FilterButtonRemovable/FilterButtonRemovable.cjs');
7
8
  var MenuTriggerProvider = require('../../../context/MenuTriggerProvider/MenuTriggerProvider.cjs');
@@ -3,6 +3,7 @@
3
3
  var tslib = require('tslib');
4
4
  var React = require('react');
5
5
  var classnames = require('classnames');
6
+ require('../../../../FilterButton/subcomponents/FilterButtonBase/FilterButtonBase.cjs');
6
7
  require('../../../../FilterButton/FilterButton/FilterButton.cjs');
7
8
  var FilterButtonRemovable = require('../../../../FilterButton/FilterButtonRemovable/FilterButtonRemovable.cjs');
8
9
  var MenuTriggerProvider = require('../../../context/MenuTriggerProvider/MenuTriggerProvider.cjs');
@@ -3,6 +3,7 @@
3
3
  var tslib = require('tslib');
4
4
  var React = require('react');
5
5
  var Icon = require('../../../__rc__/Icon/Icon.cjs');
6
+ require('vitest');
6
7
  var listIsActive = require('../../utils/commands/listIsActive.cjs');
7
8
  var markIsActive = require('../../utils/commands/markIsActive.cjs');
8
9
  require('prosemirror-transform');
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ var vitest = require('vitest');
4
+
5
+ // eslint-disable-next-line import/no-extraneous-dependencies
6
+ /*
7
+ ** This is used handle the JSDom type error issue you may encounter in testing
8
+ ** See https://github.com/jsdom/jsdom/issues/3002
9
+ */
10
+ var mockRangeForBoundingRect = function () {
11
+ vitest.vi.spyOn(document, 'createRange').mockImplementation(function () {
12
+ var range = new Range();
13
+ range.getBoundingClientRect = function () {
14
+ return {
15
+ x: 0,
16
+ y: 0,
17
+ bottom: 0,
18
+ height: 0,
19
+ left: 0,
20
+ right: 0,
21
+ top: 0,
22
+ width: 0,
23
+ toJSON: function () {
24
+ return undefined;
25
+ }
26
+ };
27
+ };
28
+ range.getClientRects = function () {
29
+ var _a;
30
+ return _a = {
31
+ item: function () {
32
+ return null;
33
+ },
34
+ length: 0
35
+ }, _a[Symbol.iterator] = vitest.vi.fn(), _a;
36
+ };
37
+ return range;
38
+ });
39
+ };
40
+ exports.mockRangeForBoundingRect = mockRangeForBoundingRect;
@@ -2,6 +2,7 @@
2
2
 
3
3
  var debounce = require('lodash.debounce');
4
4
  var ProseMirrorState = require('prosemirror-state');
5
+ require('vitest');
5
6
  var getMarkAttrs = require('../../commands/getMarkAttrs.cjs');
6
7
  var getMarkRange = require('../../commands/getMarkRange.cjs');
7
8
  require('prosemirror-utils');
@@ -47,6 +47,7 @@ var Filter = require('./Filter/Filter/Filter.cjs');
47
47
  var FilterContents = require('./Filter/Filter/subcomponents/FilterContents/FilterContents.cjs');
48
48
  var FilterBar = require('./Filter/FilterBar/FilterBar.cjs');
49
49
  var FilterBarContext = require('./Filter/FilterBar/context/FilterBarContext.cjs');
50
+ var FilterButtonBase = require('./Filter/FilterButton/subcomponents/FilterButtonBase/FilterButtonBase.cjs');
50
51
  var FilterButton = require('./Filter/FilterButton/FilterButton/FilterButton.cjs');
51
52
  var FilterButtonRemovable = require('./Filter/FilterButton/FilterButtonRemovable/FilterButtonRemovable.cjs');
52
53
  var FilterDatePicker = require('./Filter/FilterDatePicker/FilterDatePicker.cjs');
@@ -354,6 +355,7 @@ var RichTextEditor = require('./RichTextEditor/RichTextEditor/RichTextEditor.cjs
354
355
  var ToggleIconButton = require('./RichTextEditor/RichTextEditor/subcomponents/ToggleIconButton/ToggleIconButton.cjs');
355
356
  var Toolbar = require('./RichTextEditor/RichTextEditor/subcomponents/Toolbar/Toolbar.cjs');
356
357
  var ToolbarSection = require('./RichTextEditor/RichTextEditor/subcomponents/ToolbarSection/ToolbarSection.cjs');
358
+ var mockRangeForBoundingRect = require('./RichTextEditor/utils/commands/fixtures/mockRangeForBoundingRect.cjs');
357
359
  var addMark = require('./RichTextEditor/utils/commands/addMark.cjs');
358
360
  var getMarkAttrs = require('./RichTextEditor/utils/commands/getMarkAttrs.cjs');
359
361
  var getMarkRange = require('./RichTextEditor/utils/commands/getMarkRange.cjs');
@@ -488,6 +490,7 @@ exports.FilterContents = FilterContents.FilterContents;
488
490
  exports.FilterBar = FilterBar.FilterBar;
489
491
  exports.FilterBarProvider = FilterBarContext.FilterBarProvider;
490
492
  exports.useFilterBarContext = FilterBarContext.useFilterBarContext;
493
+ exports.FilterButtonBase = FilterButtonBase.FilterButtonBase;
491
494
  exports.FilterButton = FilterButton.FilterButton;
492
495
  exports.FilterButtonRemovable = FilterButtonRemovable.FilterButtonRemovable;
493
496
  exports.FilterDatePicker = FilterDatePicker.FilterDatePicker;
@@ -979,6 +982,7 @@ exports.RichTextEditor = RichTextEditor.RichTextEditor;
979
982
  exports.ToggleIconButton = ToggleIconButton.ToggleIconButton;
980
983
  exports.Toolbar = Toolbar.Toolbar;
981
984
  exports.ToolbarSection = ToolbarSection.ToolbarSection;
985
+ exports.mockRangeForBoundingRect = mockRangeForBoundingRect.mockRangeForBoundingRect;
982
986
  exports.addMark = addMark.addMark;
983
987
  exports.getMarkAttrs = getMarkAttrs.getMarkAttrs;
984
988
  exports.getMarkRange = getMarkRange.getMarkRange;
@@ -2,6 +2,7 @@ import { __rest, __assign } from 'tslib';
2
2
  import React, { forwardRef, useEffect } from 'react';
3
3
  import classnames from 'classnames';
4
4
  import { useFilterBarContext } from '../../context/FilterBarContext.mjs';
5
+ import '../../../FilterButton/subcomponents/FilterButtonBase/FilterButtonBase.mjs';
5
6
  import { FilterButton } from '../../../FilterButton/FilterButton/FilterButton.mjs';
6
7
  import { FilterButtonRemovable } from '../../../FilterButton/FilterButtonRemovable/FilterButtonRemovable.mjs';
7
8
  import { isRefObject } from '../../../../utils/isRefObject.mjs';
@@ -1,5 +1,6 @@
1
1
  import { __assign } from 'tslib';
2
2
  import React, { useRef } from 'react';
3
+ import '../../../../FilterButton/subcomponents/FilterButtonBase/FilterButtonBase.mjs';
3
4
  import { FilterButton } from '../../../../FilterButton/FilterButton/FilterButton.mjs';
4
5
  import '../../../../FilterButton/FilterButtonRemovable/FilterButtonRemovable.mjs';
5
6
  import { useMenuTriggerContext } from '../../../context/MenuTriggerProvider/MenuTriggerProvider.mjs';
@@ -1,6 +1,7 @@
1
1
  import { __assign } from 'tslib';
2
2
  import React, { useRef } from 'react';
3
3
  import classnames from 'classnames';
4
+ import '../../../../FilterButton/subcomponents/FilterButtonBase/FilterButtonBase.mjs';
4
5
  import '../../../../FilterButton/FilterButton/FilterButton.mjs';
5
6
  import { FilterButtonRemovable } from '../../../../FilterButton/FilterButtonRemovable/FilterButtonRemovable.mjs';
6
7
  import { useMenuTriggerContext } from '../../../context/MenuTriggerProvider/MenuTriggerProvider.mjs';
@@ -1,6 +1,7 @@
1
1
  import { __assign } from 'tslib';
2
2
  import React from 'react';
3
3
  import { Icon } from '../../../__rc__/Icon/Icon.mjs';
4
+ import 'vitest';
4
5
  import { listIsActive } from '../../utils/commands/listIsActive.mjs';
5
6
  import { markIsActive } from '../../utils/commands/markIsActive.mjs';
6
7
  import 'prosemirror-transform';
@@ -0,0 +1,38 @@
1
+ import { vi } from 'vitest';
2
+
3
+ // eslint-disable-next-line import/no-extraneous-dependencies
4
+ /*
5
+ ** This is used handle the JSDom type error issue you may encounter in testing
6
+ ** See https://github.com/jsdom/jsdom/issues/3002
7
+ */
8
+ var mockRangeForBoundingRect = function () {
9
+ vi.spyOn(document, 'createRange').mockImplementation(function () {
10
+ var range = new Range();
11
+ range.getBoundingClientRect = function () {
12
+ return {
13
+ x: 0,
14
+ y: 0,
15
+ bottom: 0,
16
+ height: 0,
17
+ left: 0,
18
+ right: 0,
19
+ top: 0,
20
+ width: 0,
21
+ toJSON: function () {
22
+ return undefined;
23
+ }
24
+ };
25
+ };
26
+ range.getClientRects = function () {
27
+ var _a;
28
+ return _a = {
29
+ item: function () {
30
+ return null;
31
+ },
32
+ length: 0
33
+ }, _a[Symbol.iterator] = vi.fn(), _a;
34
+ };
35
+ return range;
36
+ });
37
+ };
38
+ export { mockRangeForBoundingRect };
@@ -1,5 +1,6 @@
1
1
  import debounce from 'lodash.debounce';
2
2
  import { Plugin } from 'prosemirror-state';
3
+ import 'vitest';
3
4
  import { getMarkAttrs } from '../../commands/getMarkAttrs.mjs';
4
5
  import { getMarkRange } from '../../commands/getMarkRange.mjs';
5
6
  import 'prosemirror-utils';
@@ -45,6 +45,7 @@ export { Filter } from './Filter/Filter/Filter.mjs';
45
45
  export { FilterContents } from './Filter/Filter/subcomponents/FilterContents/FilterContents.mjs';
46
46
  export { FilterBar } from './Filter/FilterBar/FilterBar.mjs';
47
47
  export { FilterBarProvider, useFilterBarContext } from './Filter/FilterBar/context/FilterBarContext.mjs';
48
+ export { FilterButtonBase } from './Filter/FilterButton/subcomponents/FilterButtonBase/FilterButtonBase.mjs';
48
49
  export { FilterButton } from './Filter/FilterButton/FilterButton/FilterButton.mjs';
49
50
  export { FilterButtonRemovable } from './Filter/FilterButton/FilterButtonRemovable/FilterButtonRemovable.mjs';
50
51
  export { FilterDatePicker } from './Filter/FilterDatePicker/FilterDatePicker.mjs';
@@ -352,6 +353,7 @@ export { RichTextEditor } from './RichTextEditor/RichTextEditor/RichTextEditor.m
352
353
  export { ToggleIconButton } from './RichTextEditor/RichTextEditor/subcomponents/ToggleIconButton/ToggleIconButton.mjs';
353
354
  export { Toolbar } from './RichTextEditor/RichTextEditor/subcomponents/Toolbar/Toolbar.mjs';
354
355
  export { ToolbarSection } from './RichTextEditor/RichTextEditor/subcomponents/ToolbarSection/ToolbarSection.mjs';
356
+ export { mockRangeForBoundingRect } from './RichTextEditor/utils/commands/fixtures/mockRangeForBoundingRect.mjs';
355
357
  export { addMark } from './RichTextEditor/utils/commands/addMark.mjs';
356
358
  export { getMarkAttrs } from './RichTextEditor/utils/commands/getMarkAttrs.mjs';
357
359
  export { getMarkRange } from './RichTextEditor/utils/commands/getMarkRange.mjs';
@@ -9,7 +9,7 @@ export declare const FilterBarButton: React.ForwardRefExoticComponent<{
9
9
  label: string;
10
10
  selectedValue?: string | JSX.Element;
11
11
  isOpen?: boolean;
12
- } & Omit<import("../../../FilterButton/subcomponents/FilterButtonBase").FilterButtonBaseProps, "children"> & {
12
+ } & Omit<import("../../../FilterButton").FilterButtonBaseProps, "children"> & {
13
13
  filterId: string;
14
14
  isRemovable: boolean | undefined;
15
15
  } & React.RefAttributes<FilterTriggerRef>>;
@@ -1,2 +1,3 @@
1
+ export * from './subcomponents/FilterButtonBase';
1
2
  export * from './FilterButton';
2
3
  export * from './FilterButtonRemovable';
@@ -1,6 +1,5 @@
1
1
  import { type EditorState, type Transaction } from 'prosemirror-state';
2
2
  import { type ProseMirrorModel } from '../../prosemirror';
3
- export declare const mockRangeForBoundingRect: () => void;
4
3
  export declare const simulateRangeSelection: (anchorPositionStart?: number, anchorPositionEnd?: number) => (state: EditorState, dispatch?: (tx: Transaction) => void) => boolean;
5
4
  export declare const getStartNode: (state: EditorState) => ReturnType<ProseMirrorModel.Node["childAfter"]>;
6
5
  export declare const simulateSelectionOfCurrentElement: (selectEntireElement?: boolean) => (state: EditorState, dispatch: (tx: Transaction) => void) => boolean;
@@ -0,0 +1,2 @@
1
+ export * from './mockRangeForBoundingRect';
2
+ export * from './helpers';
@@ -0,0 +1 @@
1
+ export declare const mockRangeForBoundingRect: () => void;
@@ -1,3 +1,4 @@
1
+ export * from './fixtures/mockRangeForBoundingRect';
1
2
  export * from './addMark';
2
3
  export * from './getMarkAttrs';
3
4
  export * from './getMarkRange';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "1.70.20",
3
+ "version": "1.70.22",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -1,2 +1,3 @@
1
+ export * from './subcomponents/FilterButtonBase'
1
2
  export * from './FilterButton'
2
3
  export * from './FilterButtonRemovable'
@@ -2,11 +2,7 @@ import { findByText, waitFor } from '@testing-library/dom'
2
2
  import { vi } from 'vitest'
3
3
  import { createRichTextEditor } from '../core'
4
4
  import { addMark } from './addMark'
5
- import {
6
- mockRangeForBoundingRect,
7
- simulateSelectionByText,
8
- simulateTextInsert,
9
- } from './fixtures/helpers'
5
+ import { mockRangeForBoundingRect, simulateSelectionByText, simulateTextInsert } from './fixtures/'
10
6
  import { testEditorState, testSchema } from './fixtures/test-state'
11
7
  describe('addMark()', () => {
12
8
  const onChange = vi.fn()
@@ -5,38 +5,7 @@ import {
5
5
  type Transaction,
6
6
  } from 'prosemirror-state'
7
7
  import { findChildrenByType } from 'prosemirror-utils'
8
- // eslint-disable-next-line import/no-extraneous-dependencies
9
- import { vi } from 'vitest'
10
8
  import { type ProseMirrorModel } from '../../prosemirror'
11
- /*
12
- ** This is used handle the JSDom type error issue you may encounter in testing
13
- ** See https://github.com/jsdom/jsdom/issues/3002
14
- */
15
- export const mockRangeForBoundingRect = (): void => {
16
- vi.spyOn(document, 'createRange').mockImplementation(() => {
17
- const range = new Range()
18
-
19
- range.getBoundingClientRect = () => ({
20
- x: 0,
21
- y: 0,
22
- bottom: 0,
23
- height: 0,
24
- left: 0,
25
- right: 0,
26
- top: 0,
27
- width: 0,
28
- toJSON: () => undefined,
29
- })
30
-
31
- range.getClientRects = () => ({
32
- item: () => null,
33
- length: 0,
34
- [Symbol.iterator]: vi.fn(),
35
- })
36
-
37
- return range
38
- })
39
- }
40
9
 
41
10
  /*
42
11
  ** Prosemirror cannot intuit a real `Selection` of content injected into the base test.
@@ -0,0 +1,2 @@
1
+ export * from './mockRangeForBoundingRect'
2
+ export * from './helpers'
@@ -0,0 +1,32 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+ import { vi } from 'vitest'
3
+
4
+ /*
5
+ ** This is used handle the JSDom type error issue you may encounter in testing
6
+ ** See https://github.com/jsdom/jsdom/issues/3002
7
+ */
8
+ export const mockRangeForBoundingRect = (): void => {
9
+ vi.spyOn(document, 'createRange').mockImplementation(() => {
10
+ const range = new Range()
11
+
12
+ range.getBoundingClientRect = () => ({
13
+ x: 0,
14
+ y: 0,
15
+ bottom: 0,
16
+ height: 0,
17
+ left: 0,
18
+ right: 0,
19
+ top: 0,
20
+ width: 0,
21
+ toJSON: () => undefined,
22
+ })
23
+
24
+ range.getClientRects = () => ({
25
+ item: () => null,
26
+ length: 0,
27
+ [Symbol.iterator]: vi.fn(),
28
+ })
29
+
30
+ return range
31
+ })
32
+ }
@@ -1,3 +1,4 @@
1
+ export * from './fixtures/mockRangeForBoundingRect'
1
2
  export * from './addMark'
2
3
  export * from './getMarkAttrs'
3
4
  export * from './getMarkRange'
@@ -1,6 +1,10 @@
1
1
  import { Canvas, Meta, Controls } from '@storybook/blocks'
2
- import { ResourceLinks, KAIOInstallation } from '~storybook/components'
2
+ import { ResourceLinks, KAIOInstallation, DosAndDonts, DoOrDont } from '~storybook/components'
3
3
  import * as Button from './Button.docs.stories'
4
+ import ButtonIconOnlySpec from './assets/button_icon_only_spec.png'
5
+ import ButtonIconSpec from './assets/button_icon_spec.png'
6
+ import ButtonSpec from './assets/button_spec.png'
7
+ import ButtonAnatomy from './assets/button_anatomy.png'
4
8
 
5
9
  <Meta title="Components/Button/Button (v3)/Usage Guidelines" />
6
10
 
@@ -27,3 +31,168 @@ Buttons allow users to perform an action. They have multiple styles for various
27
31
  include={['children', 'variant', 'size', 'isDisabled', 'icon', 'iconPosition']}
28
32
  className="mb-64"
29
33
  />
34
+
35
+ ## Anatomy
36
+
37
+ <img
38
+ src={ButtonAnatomy}
39
+ alt="Indicates the anatomy of an Button component with text"
40
+ className="my-auto"
41
+ />
42
+
43
+ ### When to use
44
+
45
+ Use buttons to communicate actions users can take. Each page should have only one primary button, and any remaining calls to action should use lower emphasis buttons.
46
+
47
+ ### When not to use
48
+
49
+ Do not use buttons as navigational elements. Instead, use links when the desired action is to take the user to a new page.
50
+
51
+ ## Specs
52
+
53
+ <img
54
+ src={ButtonSpec}
55
+ alt="Indicates the spacing specifications of a Button component with text"
56
+ className="my-auto"
57
+ />
58
+
59
+ <img
60
+ src={ButtonIconSpec}
61
+ alt="Indicates the spacing specifications of a Button component with text and an icon"
62
+ className="my-auto"
63
+ />
64
+
65
+ <img
66
+ src={ButtonIconOnlySpec}
67
+ alt="Indicates the spacing specifications of an icon-only Button"
68
+ className="my-auto"
69
+ />
70
+
71
+ ### Dos and don'ts
72
+
73
+ #### Use primary buttons to highlight a single, important action
74
+
75
+ Reserve the primary button for actions that are essential to an experience. Usually, use only one primary button on a page or section. These buttons give extra prominence and help establish a clear hierarchy.
76
+
77
+ <DosAndDonts>
78
+ <DoOrDont story={Button.DoExamplePrimaryAndSecondary} />
79
+ <DoOrDont story={Button.DontExamplePrimaryAndSecondary} isDont />
80
+ </DosAndDonts>
81
+
82
+ #### Use secondary buttons for medium emphasis actions
83
+
84
+ Use secondary buttons when the action requires less prominence, or if there are multiple actions with the same importance on a page or section.
85
+
86
+ #### Use tertiary buttons to communicate action hierarchy
87
+
88
+ Use tertiary buttons for less important actions within a group or section to help establish a clear hierarchy. Don't use tertiary buttons in isolation.
89
+
90
+ #### Use large buttons for high emphasis
91
+
92
+ Use large buttons sparingly within pages or sections where you need a high emphasis and prominent button to provide balance with other large elements. This might be on a landing page or a CTA in proximity to large headings.
93
+
94
+ <DosAndDonts>
95
+ <DoOrDont story={Button.DoExampleCta} />
96
+ </DosAndDonts>
97
+
98
+ #### Use medium buttons most of the time
99
+
100
+ Medium buttons are appropriate in most common contexts such as pairing with input fields.
101
+
102
+ <DosAndDonts>
103
+ <DoOrDont story={Button.DoExampleDefaultToMediumSize} />
104
+ <DoOrDont story={Button.DontExampleDefaultToMediumSize} isDont />
105
+ </DosAndDonts>
106
+
107
+ #### Use small buttons in information rich contexts
108
+
109
+ Use small buttons when space is limited, such as inside or adjacent to lists, tables, or data visualizations.
110
+
111
+ Ensure clarity by prioritizing the most important actions. Think about how many buttons you use and where you put them. Don't overload your interface with too many buttons, as clustering buttons can confuse people.
112
+
113
+ #### Use icons in Primary and Secondary buttons sparingly and consistently
114
+
115
+ Overusing icons can create visual clutter and overwhelm users. Use them sparingly to highlight common and recognizable actions.
116
+
117
+ <DosAndDonts>
118
+ <DoOrDont story={Button.DoExampleUseIconsSparingly} />
119
+ <DoOrDont story={Button.DontExampleUseIconsSparingly} isDont />
120
+ </DosAndDonts>
121
+
122
+ #### Always use icons in Tertiary buttons
123
+
124
+ Without icons, tertiary buttons can look like plain text. Tertiary buttons must always use icons to support a button affordance and meet accessibility standards.
125
+
126
+ <DosAndDonts>
127
+ <DoOrDont story={Button.DoExampleTertiaryButtonWithIcons} />
128
+ <DoOrDont story={Button.DontExampleTertiaryButtonWithIcons} isDont />
129
+ </DosAndDonts>
130
+
131
+ #### Use tooltips when the label is hidden
132
+
133
+ When a button label is hidden and only an icon is used, show a tooltip on hover that displays the label text.
134
+
135
+ <DosAndDonts>
136
+ <DoOrDont story={Button.DoExampleUseTooltips} />
137
+ <DoOrDont story={Button.DontExampleUseTooltips} isDont />
138
+ </DosAndDonts>
139
+
140
+ #### Use a menu button when all actions are equally important
141
+
142
+ Group related actions in a menu. The action options should be closely related and make sense as a group. You can use any type or size of button based on the context.
143
+
144
+ <DosAndDonts>
145
+ <DoOrDont story={Button.DoExampleMenuButton} />
146
+ </DosAndDonts>
147
+
148
+ #### Use a button and an overflow button when there is a most common or distinct action
149
+
150
+ Highlight the most important, common, or a distinct action by using a button with text. Group related, less important actions in an overflow button. The action options should be closely related and make sense as a group. You can use any type or size of button based on the context. This pattern replaces Split Buttons.
151
+
152
+ <DosAndDonts>
153
+ <DoOrDont story={Button.DoExampleKebabMenuButton} />
154
+ </DosAndDonts>
155
+
156
+ #### Write clear, action-oriented button labels
157
+
158
+ Write button labels as concise verbs that clearly state the action and its outcome. Always align the button label with the language used elsewhere in the Platform to maintain consistency and clarity.
159
+ Make sure that all your buttons begin with a strong verb (something that elicits an action from the user). Verbs indicate to a user what they’re about to see or do next.
160
+
161
+ <DosAndDonts>
162
+ <DoOrDont story={Button.DoExampleClearAndConciseLabels} />
163
+ <DoOrDont story={Button.DontExampleClearAndConciseLabels} isDont />
164
+ </DosAndDonts>
165
+
166
+ A verb and noun combination gives users more context and can help guide them when the action they’re about to perform isn’t commonplace.
167
+
168
+ <DosAndDonts>
169
+ <DoOrDont story={Button.DoExampleDeclareContext} />
170
+ <DoOrDont story={Button.DontExampleDeclareContext} isDont />
171
+ </DosAndDonts>
172
+
173
+ #### Write in sentence case with minimal punctuation
174
+
175
+ All buttons should be in sentence case i.e. the first letter of the first word is capitalized and everything else is in lower case unless it’s a proper noun or feature name. Always err on the side of minimal punctuation, so leave out full stops and commas where possible.
176
+
177
+ <DosAndDonts>
178
+ <DoOrDont story={Button.DoExampleMinimalPunctuation} />
179
+ <DoOrDont story={Button.DontExampleMinimalPunctuation} isDont />
180
+ </DosAndDonts>
181
+
182
+ #### Use second person
183
+
184
+ Any buttons that include a personal pronoun should be written in the second person. Keep in mind that you shouldn’t use personal pronouns unless you’re asking the user to filter something. For example, “View your reports” vs “View all reports.”
185
+
186
+ <DosAndDonts>
187
+ <DoOrDont story={Button.DoExampleSecondPerson} />
188
+ <DoOrDont story={Button.DontExampleSecondPerson} isDont />
189
+ </DosAndDonts>
190
+
191
+ #### Button labels should make sense in isolation
192
+
193
+ If buttons on a page have the same label repeated multiple times, they should have distinct, accessible names that add context for users. This ensures better accessibility for screen reader users, who rely on descriptive labels to distinguish between actions.
194
+
195
+ <DosAndDonts>
196
+ <DoOrDont story={Button.DoExampleFunctionalLabels} />
197
+ <DoOrDont story={Button.DontExampleFunctionalLabels} isDont />
198
+ </DosAndDonts>
@@ -2,7 +2,17 @@ import React from 'react'
2
2
  import { action } from '@storybook/addon-actions'
3
3
  import { type Meta, type StoryObj } from '@storybook/react'
4
4
  import { Badge } from '~components/Badge'
5
- import { Icon } from '~components/__rc__'
5
+ import { EmptyState } from '~components/EmptyState'
6
+ import {
7
+ Icon,
8
+ Menu,
9
+ MenuItem,
10
+ MenuPopover,
11
+ MenuTrigger,
12
+ Select,
13
+ Tooltip,
14
+ TooltipTrigger,
15
+ } from '~components/__rc__'
6
16
  import { ReversedColors } from '~components/__utilities__/v3'
7
17
  import { Button } from '../index'
8
18
 
@@ -15,7 +25,7 @@ const meta = {
15
25
  },
16
26
  argTypes: {
17
27
  icon: {
18
- options: ['delete', 'arrow', 'plus'],
28
+ options: ['delete', 'arrow', 'add'],
19
29
  mapping: {
20
30
  delete: <Icon isPresentational name="delete" />,
21
31
  arrow: <Icon isPresentational name="arrow_forward" />,
@@ -60,13 +70,6 @@ export const ButtonVariantsReversed: Story = {
60
70
  reverseColors: true,
61
71
  backgrounds: { default: 'Purple 700' },
62
72
  },
63
- decorators: [
64
- (Story) => (
65
- <div className="flex gap-8">
66
- <Story />
67
- </div>
68
- ),
69
- ],
70
73
  }
71
74
 
72
75
  export const ButtonSizes: Story = {
@@ -136,3 +139,255 @@ export const ButtonWithBadge: Story = {
136
139
  ),
137
140
  },
138
141
  }
142
+
143
+ // Dos and Donts
144
+
145
+ export const DoExamplePrimaryAndSecondary: Story = {
146
+ render: () => {
147
+ return (
148
+ <>
149
+ <Button variant="primary" className="me-8">
150
+ Submit
151
+ </Button>
152
+ <Button variant="secondary">Cancel</Button>
153
+ </>
154
+ )
155
+ },
156
+ decorators: [
157
+ (Story) => (
158
+ <div className="flex gap-8">
159
+ <Story />
160
+ </div>
161
+ ),
162
+ ],
163
+ }
164
+
165
+ export const DontExamplePrimaryAndSecondary: Story = {
166
+ render: () => {
167
+ return (
168
+ <>
169
+ <Button variant="primary" className="me-8">
170
+ Submit
171
+ </Button>
172
+ <Button variant="primary">Cancel</Button>
173
+ </>
174
+ )
175
+ },
176
+ decorators: [
177
+ (Story) => (
178
+ <div className="flex gap-8">
179
+ <Story />
180
+ </div>
181
+ ),
182
+ ],
183
+ }
184
+
185
+ export const DoExampleCta: Story = {
186
+ render: () => {
187
+ return (
188
+ <>
189
+ <EmptyState
190
+ headingProps={{ children: 'Lorem', variant: 'heading-2', tag: 'span' }}
191
+ bodyText="Ipsum dolor sit amet consectetur adipisicing elit."
192
+ >
193
+ <Button size="large">Action</Button>
194
+ </EmptyState>
195
+ </>
196
+ )
197
+ },
198
+ }
199
+
200
+ export const DoExampleDefaultToMediumSize: Story = {
201
+ render: () => {
202
+ return (
203
+ <>
204
+ <Select items={[]} label="Choose country"></Select>
205
+ <Button className="mt-8">Save</Button>
206
+ </>
207
+ )
208
+ },
209
+ }
210
+
211
+ export const DontExampleDefaultToMediumSize: Story = {
212
+ render: () => {
213
+ return (
214
+ <>
215
+ <Select items={[]} label="Choose country"></Select>
216
+ <Button className="mt-8" variant="primary" size="small">
217
+ Save
218
+ </Button>
219
+ </>
220
+ )
221
+ },
222
+ }
223
+
224
+ export const DoExampleUseIconsSparingly: Story = {
225
+ args: {
226
+ children: 'Add user',
227
+ icon: <Icon isPresentational name="add" />,
228
+ iconPosition: 'end',
229
+ },
230
+ }
231
+
232
+ export const DontExampleUseIconsSparingly: Story = {
233
+ args: {
234
+ children: 'Add user',
235
+ icon: <Icon isPresentational name="star" />,
236
+ iconPosition: 'end',
237
+ },
238
+ }
239
+
240
+ export const DoExampleUseTooltips: Story = {
241
+ args: {
242
+ children: 'Delete',
243
+ icon: <Icon isPresentational name="delete" />,
244
+ hasHiddenLabel: true,
245
+ variant: 'tertiary',
246
+ },
247
+ render: (args) => (
248
+ <TooltipTrigger>
249
+ <Button {...args} />
250
+ <Tooltip>Delete content</Tooltip>
251
+ </TooltipTrigger>
252
+ ),
253
+ }
254
+
255
+ export const DontExampleUseTooltips: Story = {
256
+ args: {
257
+ children: 'Delete',
258
+ icon: <Icon isPresentational name="delete" />,
259
+ hasHiddenLabel: true,
260
+ variant: 'tertiary',
261
+ },
262
+ }
263
+
264
+ export const DoExampleMenuButton: Story = {
265
+ render: () => (
266
+ <MenuTrigger defaultOpen={false}>
267
+ <Button
268
+ variant="secondary"
269
+ icon={<Icon name="keyboard_arrow_down" isPresentational />}
270
+ iconPosition="end"
271
+ >
272
+ Manage
273
+ </Button>
274
+ <MenuPopover>
275
+ <Menu>
276
+ <MenuItem icon={<Icon name="bookmark" isPresentational />}>Save</MenuItem>
277
+ <MenuItem icon={<Icon name="edit" isPresentational isFilled />}>Edit</MenuItem>
278
+ <MenuItem icon={<Icon name="delete" isPresentational isFilled />}>Delete</MenuItem>
279
+ </Menu>
280
+ </MenuPopover>
281
+ </MenuTrigger>
282
+ ),
283
+ }
284
+
285
+ export const DoExampleKebabMenuButton: Story = {
286
+ render: () => (
287
+ <TooltipTrigger>
288
+ <MenuTrigger defaultOpen={false}>
289
+ <Button
290
+ variant="secondary"
291
+ icon={<Icon name="more_horiz" isPresentational />}
292
+ iconPosition="end"
293
+ hasHiddenLabel
294
+ >
295
+ Manage
296
+ </Button>
297
+ <MenuPopover>
298
+ <Menu>
299
+ <MenuItem icon={<Icon name="bookmark" isPresentational />}>Save</MenuItem>
300
+ <MenuItem icon={<Icon name="edit" isPresentational isFilled />}>Edit</MenuItem>
301
+ <MenuItem icon={<Icon name="delete" isPresentational isFilled />}>Delete</MenuItem>
302
+ </Menu>
303
+ </MenuPopover>
304
+ </MenuTrigger>
305
+ <Tooltip>Manage</Tooltip>
306
+ </TooltipTrigger>
307
+ ),
308
+ }
309
+
310
+ export const DoExampleMinimalPunctuation: Story = {
311
+ args: {
312
+ children: 'View tour',
313
+ variant: 'secondary',
314
+ },
315
+ }
316
+
317
+ export const DontExampleMinimalPunctuation: Story = {
318
+ args: {
319
+ children: "What's new?",
320
+ variant: 'secondary',
321
+ },
322
+ }
323
+
324
+ export const DoExampleSecondPerson: Story = {
325
+ args: {
326
+ children: 'View your reports',
327
+ variant: 'secondary',
328
+ },
329
+ }
330
+
331
+ export const DontExampleSecondPerson: Story = {
332
+ args: {
333
+ children: 'View my reports',
334
+ variant: 'secondary',
335
+ },
336
+ }
337
+
338
+ export const DoExampleFunctionalLabels: Story = {
339
+ args: {
340
+ children: 'Edit report',
341
+ variant: 'secondary',
342
+ },
343
+ }
344
+
345
+ export const DontExampleFunctionalLabels: Story = {
346
+ args: {
347
+ children: 'Edit',
348
+ variant: 'secondary',
349
+ },
350
+ }
351
+
352
+ export const DoExampleClearAndConciseLabels: Story = {
353
+ args: {
354
+ children: 'Save',
355
+ variant: 'secondary',
356
+ },
357
+ }
358
+
359
+ export const DontExampleClearAndConciseLabels: Story = {
360
+ args: {
361
+ children: 'Go to next step',
362
+ variant: 'secondary',
363
+ },
364
+ }
365
+
366
+ export const DoExampleDeclareContext: Story = {
367
+ args: {
368
+ children: 'Create survey',
369
+ variant: 'secondary',
370
+ },
371
+ }
372
+
373
+ export const DontExampleDeclareContext: Story = {
374
+ args: {
375
+ children: 'Create',
376
+ variant: 'secondary',
377
+ },
378
+ }
379
+
380
+ export const DoExampleTertiaryButtonWithIcons: Story = {
381
+ args: {
382
+ children: 'Edit cycle',
383
+ variant: 'tertiary',
384
+ icon: <Icon isPresentational name="edit" />,
385
+ },
386
+ }
387
+
388
+ export const DontExampleTertiaryButtonWithIcons: Story = {
389
+ args: {
390
+ children: 'Edit cycle',
391
+ variant: 'tertiary',
392
+ },
393
+ }